如何判断两个数组是否具有相同的值

187

我有两个数组:一个用于存储AJAX请求返回的信息,另一个则记录用户点击的按钮。我使用以下代码(示例数字已填充):

var array1 = [2, 4];
var array2 = [4, 2]; //It cames from the user button clicks, so it might be disordered.
array1.sort(); //Sorts both Ajax and user info.
array2.sort();
if (array1==array2) {
    doSomething();
}else{
    doAnotherThing();
}

但是它总是返回false,即使两个数组相同,只是名称不同。(我在Chrome的JS控制台中检查了这一点)。那么有没有办法可以知道这两个数组是否相同?为什么会返回false?我如何知道第一个数组中哪些值不在第二个数组中?


1
我非常确定您需要遍历数组的每个元素。 - Thomas Li
你知道为什么它返回false吗?好奇。 - RobW
请查看 @Andrew 的答案 https://dev59.com/om025IYBdhLWcg3wFxqa#6229263 - Carlos Precioso
可能是重复的问题:如何检查 JavaScript 数组是否相等? - Palec
32个回答

155

如果你的数组项不是对象,例如数字或字符串,你可以将它们连接成字符串并进行比较,以确定它们是否具有相同的成员(任意顺序)。

var array1= [10, 6, 19, 16, 14, 15, 2, 9, 5, 3, 4, 13, 8, 7, 1, 12, 18, 11, 20, 17];
var array2= [12, 18, 20, 11, 19, 14, 6, 7, 8, 16, 9, 3, 1, 13, 5, 4, 15, 10, 2, 17];

if(array1.sort().join(',')=== array2.sort().join(',')){
    alert('same members');
}
else alert('not a match');

6
这对于具有唯一标识toString值的原始类型或对象将有效,但不适用于任何对象。 - devios1
4
注意空项和排序。在我的情况下,我最终需要比较的字符串是",2,2,3"和"2,2,3,",它们显然不是严格相等的。 - barbara.post
19
该技术对于字符串可能会失败,例如 ['a', 'b']['a,b']。我只建议在小的一次性脚本中使用这种技术。 - alex
1
@alex -- 在我的情况下,字符串中允许使用逗号,但不允许使用分号,因此我使用 ';' 来连接。 - a2345sooted
这太糟糕了。比任何其他方法慢高达1000倍。 - gman
显示剩余3条评论

66

如果你想检查两个数组是否具有相同的值(不考虑每个值的出现次数和顺序),可以使用lodash实现:

_.isEmpty(_.xor(array1, array2))

简洁、简单而且漂亮!


1
我在 Underscore 文档中找不到 xor?你是不是想说 IODash? - Patrick Mencias-lewis
这只适用于整数。问题是“两个数组”,而不是“两个整数数组”。 - gman
3
没错。在2021年,并且确认它适用于字符串数组。 :-) - Murrah

59

对数组进行排序,逐个比较它们的值。

function arrayCompare(_arr1, _arr2) {
    if (
      !Array.isArray(_arr1)
      || !Array.isArray(_arr2)
      || _arr1.length !== _arr2.length
      ) {
        return false;
      }
    
    // .concat() to not mutate arguments
    const arr1 = _arr1.concat().sort();
    const arr2 = _arr2.concat().sort();
    
    for (let i = 0; i < arr1.length; i++) {
        if (arr1[i] !== arr2[i]) {
            return false;
         }
    }
    
    return true;
}

7
排序需要 nlog(n) 的时间。你不需要进行排序。这个答案 https://dev59.com/om025IYBdhLWcg3wFxqa#55614659 可以在线性时间内解决问题。 - canbax
3
@canbax提供的函数在我的情况下,当数组为[1,2]和[1,1,2]时返回true,这两个数组在我的情况下是不同的数组。但是,该函数对这两个不同的数组返回false。 - Lukas Liesis
1
@LukasLiesis 这个问题问的是“如果两个数组具有相同的值”,所以它并不是在问它们是否相同。我理解它是在问这些数组是否相等。https://en.wikipedia.org/wiki/Set_(mathematics)。 [1,2][1,1,2] 是相等的集合。它们都包含 12 - canbax
@YevheniiShlapak 因为这些数组只共享一个值... - Maciej Krawczyk
1
@canbax:你链接的答案可能不是这里的正确答案,但它完美地适用于我的用例 :) - awe
显示剩余3条评论

47

为什么你的代码不起作用

在JavaScript中,对于像数组这样的非原始数据类型,=====检查引用相等性(忽略==可能执行的类型转换)。也就是说,它们检查arr1arr2是否是内存中的同一个对象。在你的例子中,这两个数组具有相同的元素并且顺序相同,但它们并不相等。

下面是一个例子,希望能够建立直觉,即[1] === [1]的结果是false

const x = [1];
const y = [1];
x === y; // false
x[0] = 200;
console.log(x); // "Array [ 200 ]"
console.log(y); // "Array [ 1 ]"
x === y; // false

解决方案

如果且仅当以下条件成立时,数组arr1arr2具有相同的成员:

  • arr2中的所有内容都在arr1

并且

  • arr1中的所有内容都在arr2

因此,这将是解决问题的方法(ES2016):

const containsAll = (arr1, arr2) => 
                arr2.every(arr2Item => arr1.includes(arr2Item))
                
const sameMembers = (arr1, arr2) => 
                        containsAll(arr1, arr2) && containsAll(arr2, arr1);

sameMembers(arr1, arr2); // `true`

这第二种使用 Underscore 的解决方案更接近于你想要的:

arr1.sort();
arr2.sort();

_.isEqual(arr1, arr2); // `true`

它能够工作是因为 isEqual 检查“深度相等”,这意味着它不仅考虑引用相等,还比较值。

你第三个问题的解决方案

你还问如何找出 arr1 中不包含在 arr2 中的元素。

这是一个解决方法 (ES2015):

const arr1 = [1, 2, 3, 4];
const arr2 = [3, 2, 1];

arr1.filter(arr1Item => !arr2.includes(arr1Item)); // `[4]`

你也可以使用Underscore的difference方法:

_.difference(arr1, arr2); // `[4]`

更新

请参考@Redu的评论——我的解决方案适用于sameMembers,但您可能想要的是sameMembersInOrder,也称为deepEquals

更新2

如果您不关心数组成员的顺序,ES2015+的Set可能比Array更好。请参阅MDN有关如何使用危险的猴子补丁实现isSupersetdifference的说明

更新3

如果顺序不重要,则仍然最好使用集合。但是,如果必须使用数组,则此解决方案的时间复杂度比我之前给出的解决方案更好:

function sameMembers(arr1, arr2) {
    const set1 = new Set(arr1);
    const set2 = new Set(arr2);
    return arr1.every(item => set2.has(item)) &&
        arr2.every(item => set1.has(item))
}

2
你的解决方案是错误的。"如果且仅当arr2中的所有元素都在arr1中,且arr1中的所有元素都在arr2中时,两个数组arr1和arr2具有相同的成员"这也是错误的。这是一个数组而不是一个集合。因此,sameMembers([1,1,2],[2,1,2]);应该返回false。 - Redu
3
@Redu 猜想这取决于“相同成员”的含义——我认为它意味着“具有相同的成员”。在我看来,sameMembers([1,1,2],[2,1,2]) 应该返回 truesameMembersInOrder([1,1,2],[2,1,2]) 也就是 deepEquals([1,1,2],[2,1,2]) 应该返回 false - Max Heiber
你的第三个解决方案 arr1.filter... 只能用于检查 arr2 是否包含 arr1 的所有元素,但不能反过来检查,而这也是必需的。 - Aakash Verma
我的袋子里有2个苹果和1个橙子。你的袋子里有2个橙子和1个苹果。它们的成员不相同。[1,1,2]和[2,1,2]不是相同的成员。 - gman
对我来说,这似乎并不是检查顺序的问题,而是每个离散值出现次数相同的问题。因此,sameMembers([1,1,2], [1,2,1]) 应返回 true,而 sameMembers(1,1,2],[1,2,1]) 应返回 false。如果我们知道不会有任何重复项,则此算法将起作用。 - Chris Walker
显示剩余2条评论

43
Array.prototype.compare = function(testArr) {
    if (this.length != testArr.length) return false;
    for (var i = 0; i < testArr.length; i++) {
        if (this[i].compare) { //To test values in nested arrays
            if (!this[i].compare(testArr[i])) return false;
        }
        else if (this[i] !== testArr[i]) return false;
    }
    return true;
}

var array1 = [2, 4];
var array2 = [4, 2];
if(array1.sort().compare(array2.sort())) {
    doSomething();
} else {
    doAnotherThing();
}
也许呢?

谢谢!它的工作方式正如所期望的那样。我稍微修改了一下函数,以便我也可以知道有多少不匹配。 - Carlos Precioso
false for [2,4] [4,2]. - Suraz Khanal
@SurazKhanal 还需要排序。 - Aaron McMillin
2
修改内置类型是一个不好的想法。 - Heretic Monkey
@HereticMonkey 同意 :-) (这个答案已经超过10年了,不再代表良好的实践) - isakkarlsson

18
我们的目标基本上是检查两个数组是否为相等集合。set 是数学上定义的集合。最快的排序在渐进意义下需要 O(nlog(n)) 的时间。所以如果你对一个数组进行排序,至少需要 O(nlog(n)) 的时间。但是,你可以使用字典数据结构更快地完成这个任务,其渐进时间复杂度为 O(n)(平均情况下而非最坏情况)。在JS中,字典就是一个带有键和值的对象。
/** assumes array elements are primitive types
* check whether 2 arrays are equal sets.
* @param  {} a1 is an array
* @param  {} a2 is an array
*/
function areArraysEqualSets(a1, a2) {
  const superSet = {};
  for (const i of a1) {
    const e = i + typeof i;
    superSet[e] = 1;
  }

  for (const i of a2) {
    const e = i + typeof i;
    if (!superSet[e]) {
      return false;
    }
    superSet[e] = 2;
  }

  for (let e in superSet) {
    if (superSet[e] === 1) {
      return false;
    }
  }

  return true;
}

请注意,此函数适用于基本类型的数组,并假定a1a2是数组。


8
这个函数没有处理一个数组中存在重复值的情况,例如:areArraysEqualSets([1, 2, 2], [1, 2]) - Chris Kent
1
它确实相等。我在控制台中尝试过了。 areArraysEqualSets([1, 2, 2], [1, 2]) true 从数学上讲,它们是相等的集合。 - canbax
7
这不是对问题的回答。问题没有涉及集合,而是有关数组。[1,2,2]和[1,2]不是同一个数组。这个回答会被删除,因为它不是一个有效的回答。 - gman
2
@gman 你错了,问题标题是“如何知道两个数组是否具有相同的值”。它没有明确重复值。但是我理解他基本上要求“两个数组是否相等”。因此,我仍然认为我的答案是正确的,并且是最快的工作算法。(在我写完之后没有检查新的答案)。同时,请注意,我不是在谈论javascript中的Set类。我指的Set是理论意义上的。这是一个数学定义。我已经给出了我所指的链接。 - canbax
1
这并不重要是否严格回答了问题,它仍然是一个有用的答案。我为了像这样的答案而搜索了这个问题。 - Grant Gryczan
显示剩余3条评论

17

这个怎么样?我猜想是 ES 2017:

const array1 = [1, 3, 5];
const array2 = [1, 5, 3];

const isEqual = (array1.length === array2.length) && (array1.every(val => array2.includes(val)));
console.log(isEqual);

第一个条件检查两个数组是否具有相同的长度,第二个条件检查第一个数组是否是第二个数组的子集。将这两个条件组合起来,应该会导致对两个数组的所有项进行比较,而不考虑元素的顺序。

上面的代码仅在两个数组具有非重复项时才有效。


请注意,此解决方案的运行时间为O(n²),而可以使用额外空间以O(n)的运行时间来解决此问题。 - mic
6
如果数组1中有重复的元素,我认为这会导致错误的肯定结果。在数组中,不能保证没有重复项。以上面的例子为例,如果数组1是[1,1,5],它也会返回true。 - Nicholas Hamilton
对于对象数组,如何进行检查? - Oliver D
@mic - 你能详细说明一下“使用额外空间”吗?哪一个解决方案具有如指示的O(n)运行时间? - Gifford N.
@GiffordN。https://dev59.com/om025IYBdhLWcg3wFxqa#63331703 是一个O(n)的解决方案。 - mic

14

对象相等性检查:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())

上述测试对于对象数组也适用,此时需要使用文档中记录的排序函数,文档地址为:http://www.w3schools.com/jsref/jsref_sort.asp

对于具有平坦JSON模式的小型数组可能已经足够。


9

对于ES6

var array1 = [2, 4];
var array2 = [4, 2];


let difference = array1.filter(x => !array2.includes(x));

console.log("equals", difference?.length != 0 ? false : true)

2
这里最好的直接答案。 - Duveral
在console.log中,你的三元运算符中的difference?是什么意思? - Dilunga the Great
1
@dilungasr 这是一个可能运算符。如果“差异”为空,它就不会抛出异常。在这个例子中没有多大用处...但对一些人来说是习惯性的。此外,这个答案只能单向工作,而且只适用于添加值的情况。它可能适用于某些用例,但遗憾的是并没有回答问题。 - Dudo

3
当你比较这两个数组时,你比较的是代表数组的对象,而不是它们的内容。
你需要使用一个函数来进行比较。你可以编写自己的函数,简单地循环遍历一个数组,并在检查其长度相同后将其与另一个数组进行比较。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接