javascript 使用 AngularJS 比较两个对象数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31126021/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 13:19:11  来源:igfitidea点击:

Comparing two arrays of objects using AngularJS

javascriptarraysangularjsforeachnested-loops

提问by Aaron Minnamon

I am having an issue with comparing two arrays. One is a fixed data set while the other is being generated dynamically.

我在比较两个数组时遇到问题。一个是固定的数据集,而另一个是动态生成的。

A sample of the two arrays are as follows:

两个数组的示例如下:

// Fixed list of 197 countries
$scope.countries = [
  {"name": "Afghanistan", "code": "AF"},
  {"name": "Albania", "code": "AF"},
  {"name": "Algeria", "code": "AF"},

  //...

  {"name": "Zimbabwe", "code": "ZW"}
];

//Dynamically generated list of matched countries

$scope.matches = [
  {"name": "Belgium"},
  {"name": "Ghana"}
];

At the end of the game a function will be run and a comparison of the two arrays will be made. As of right now I have tried (What seems like) almost every combination of this compare using angular.forEach and standard javascript loops. The issue comes when I try to log what countries haven't been matched.

在游戏结束时,将运行一个函数并对两个数组进行比较。到目前为止,我已经尝试过(看起来像什么)使用 angular.forEach 和标准 javascript 循环进行比较的几乎所有组合。当我尝试记录哪些国家/地区未匹配时,问题就出现了。

This is the compare function I am running.

这是我正在运行的比较功能。

$scope.compareArrays = function(){
      angular.forEach($scope.countries, function(country,name){
        angular.forEach($scope.matches, function(match){
          if (country.name !== match.name) {
            console.log(country.name);
          } else {
            console.log("MATCHED");
          }
        });
      });
};

The function will find and log the countries that havent been matched...but it is logging the entire list of unmatched countries multiple times. Specifically once for every object in the "matches" array.

该函数将查找并记录未匹配的国家/地区...但它多次记录未匹配国家/地区的整个列表。特别是对“matches”数组中的每个对象一次。

So for example if the matches array is the same as the sample above, it will log the unmatched countries list twice once with Belgium logging as "MATCHED" and the other time logging Belgium as an unmatched country (Same for Ghana but reversed obviously).

因此,例如,如果matches数组与上面的示例相同,它将记录两次未匹配的国家列表,其中比利时将记录为“MATCHED”,另一次将比利时记录为未匹配的国家(与加纳相同,但显然相反)。

I simply want it to log a list of the unmatched countries once and that is all.

我只是想让它记录一次不匹配国家的列表,仅此而已。

I'm hoping it is a simple oversight but cannot figure it out. Thank you in advance.

我希望这是一个简单的疏忽,但无法弄清楚。先感谢您。

回答by ccjmne

What you're doing here is (pseudo-code):

你在这里做的是(伪代码):

for each existing country
    for each country to be matched
        log country.name UNLESS it's a match

The thing is, even if all your existing countries AREin the matcheslist, each of your 197 existing countrieswill NOTmatch any of the 196 other.

问题是,即使所有现有的国家现在matches列表中,每个197个现有的国家匹配任何其他的196。

You actually need to be sure that each country(from the countrieslist) is not matched by ANYof the countries from matches: then and only then, it's actually an "unmatched" country.

您实际上需要确保每个country(来自countries列表)都没有与以下任何国家/地区匹配matches:只有这样,它实际上是一个“无法匹配”的国家/地区。



Here's a nice way to get that list (using Underscore.js, which I highly recommend):

这是获取该列表的好方法(使用我强烈推荐的Underscore.js):

// Fixed list of 197 countries
var countries = [
  {"name": "Afghanistan", "code": "AF"},
  {"name": "Albania", "code": "AF"},
  {"name": "Algeria", "code": "AF"},
  {"name": "Zimbabwe", "code": "ZW"}
];

// Dynamically generated list of matched countries

var matches = [
  {"name": "Albania"},
  {"name": "Ghana"}
];

// Rejecting the countries that have "some" (at least one) match in the other list.
function compareArrays(countries, matches){
    return _.reject(countries, function(country) {
        return _.some(matches, function(match) {
            return country.name === match.name;
        });
    });
};

_.each(compareArrays(countries, matches), function(unmatched) {
    console.log(unmatched);
});

// Object {name: "Afghanistan", code: "AF"}
// Object {name: "Algeria", code: "AF"}
// Object {name: "Zimbabwe", code: "ZW"}

And here is a link to a working JSFiddle.

这是一个指向工作 JSFiddle的链接。



Note that I haven't used any Angular stuff in that answer because it that problem is actually purely algorithmic related.

请注意,我没有在该答案中使用任何 Angular 内容,因为该问题实际上与算法有关。

回答by NiklasMH

I guess you can skip counting the duplicated checks by somehow start the second loop where the first one currently is. I would have done this with an index and a for-loop:

我想您可以通过以某种方式启动当前第一个循环的第二个循环来跳过对重复检查的计数。我会用一个索引和一个 for 循环来做到这一点:

$scope.compareArrays = function(){
  var c = $scope.countries;
  var m = $scope.matches;
  for(var i = 0;i < c.length;i++) {
    for(var j = i;j < m.length;j++) { // Notice the j = i;
      if (c[i].name !== m[j].name) {
        console.log(c[i].name);
      } else {
        console.log("MATCHED");
      }
    };
  });
};

I hope this answered your question, and hope you like plain JS too!

我希望这能回答你的问题,也希望你也喜欢普通的 JS!

回答by r-park

You should break it up into smaller functions. For example, create a comparison function which will return trueif countryis not in your matcheslist:

你应该把它分解成更小的函数。例如,创建一个比较函数,true如果country不在您的matches列表中,它将返回:

function isUnmatched(country, matches) {
  return matches.every(function(matched){
    return country.name !== matched.name;
  });
}

Then create a function that will call isUnmatchedfor each country and return an array of unmatched countries:

然后创建一个函数,该函数将为isUnmatched每个国家/地区调用并返回一组不匹配的国家/地区:

function getUnmatched() {
  return countries.filter(function(country){
    return isUnmatched(country, matches);
  });
}

If your environment doesn't support Array.prototype.everyand Array.prototype.filter, you can re-write the above as follows:

如果你的环境不支持Array.prototype.everyand Array.prototype.filter,你可以按照下面的方式重写上面的:

function isUnmatched(country, matches) {
  var i = matches.length;
  while (i--) {
    if (country.name === matches[i].name) {
      return false;
    }
  }
  return true;
}

function getUnmatched() {
  var unmatched = [],
      i = countries.length,
      country;

  while (i--) {
    country = countries[i];
    if (isUnmatched(country, matches)) {
      // console.log(country.name)
      unmatched.push(country);
    }
  }

  return unmatched;
}

So if your lists look like this:

因此,如果您的列表如下所示:

var countries = [
  {"name": "Afghanistan", "code": "AF"},
  {"name": "Albania", "code": "AF"},
  {"name": "Algeria", "code": "AF"}
];

var matches = [
  {"name": "Albania"},
  {"name": "Algeria"}
];

Then:

然后:

var unmatched = getUnmatched(); 
//=> [{"name": "Afghanistan", "code": "AF"}]

JSFiddle 1
JSFiddle 2

JSFiddle 1
JSFiddle 2

回答by skyboyer

  1. put all the names from matcheslist into a hash as a key:

    var index = {}; matches. forEach(name => index [name] = true;)

  2. iterate through countriesand check if current one exist in index:

    for (let country of countries) if (!index.hasOwnProperty(country. name)) console.log(country.name, ' is not included');

  3. get O(N+M) instead of O(N*M)

  1. matches列表中的所有名称作为键放入散列中:

    变量索引 = {}; 火柴。forEach(name => index [name] = true;)

  2. 迭代countries并检查当前是否存在于index

    for (let country of countries) if (!index.hasOwnProperty(country.name)) console.log(country.name, ' is not included');

  3. 得到 O(N+M) 而不是 O(N*M)