ChatGPT解决这个技术问题 Extra ChatGPT

如何比较 JavaScript 中的数组?

我想比较两个数组......理想情况下,有效。没什么特别的,如果它们相同则只有 true,否则 false。毫不奇怪,比较运算符似乎不起作用。

var a1 = [1,2,3];
var a2 = [1,2,3];
console.log(a1==a2);    // Returns false
console.log(JSON.stringify(a1)==JSON.stringify(a2));    // Returns true

JSON 对每个数组进行编码,但是是否有更快或“更好”的方法来简单地比较数组而无需遍历每个值?

您可以首先比较它们的长度,以及它们是否相等每个值。
是什么让两个数组对您来说相等?相同的元素?相同的元素顺序?仅当数组的元素可以序列化为 JSON 时,编码为 JSON 才有效。如果数组可以包含对象,你会走多远?什么时候两个对象“相等”?
@FelixKling,定义“平等”绝对是一个微妙的话题,但对于从高级语言开始使用 JavaScript 的人来说,没有像 ([] == []) == false 这样愚蠢的借口。
@AlexD 看起来数组使用引用相等,这是您所期望的。如果你不能这样做,那就太糟糕了
@AlexD 我有点想不出一种不会发生这种情况的语言。在 C++ 中,您将比较两个指针 - 错误。在 Java 中,您所做的与在 javascript 中相同。在 PHP 中,幕后的东西会在数组中循环——你称 PHP 为高级语言吗?

I
Isaiah Nelson

要比较数组,请遍历它们并比较每个值:

比较数组:

// Warn if overriding existing method
if(Array.prototype.equals)
    console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
    // if the other array is a falsy value, return
    if (!array)
        return false;

    // compare lengths - can save a lot of time 
    if (this.length != array.length)
        return false;

    for (var i = 0, l=this.length; i < l; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].equals(array[i]))
                return false;       
        }           
        else if (this[i] != array[i]) { 
            // Warning - two different object instances will never be equal: {x:20} != {x:20}
            return false;   
        }           
    }       
    return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});

用法:

[1, 2, [3, 4]].equals([1, 2, [3, 2]]) === false;
[1, "2,3"].equals([1, 2, 3]) === false;
[1, 2, [3, 4]].equals([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].equals([1, 2, 1, 2]) === true;

您可能会说“但是比较字符串要快得多 - 没有循环......”那么您应该注意有循环。第一个将 Array 转换为字符串的递归循环,第二个是比较两个字符串的递归循环。所以这种方法比使用字符串要快。

我相信大量的数据应该始终存储在数组中,而不是对象中。但是,如果您使用对象,它们也可以进行部分比较。就是这样:

比较对象:

我在上面说过,两个对象实例永远不会相等,即使它们现在包含相同的数据:

({a:1, foo:"bar", numberOfTheBeast: 666}) == ({a:1, foo:"bar", numberOfTheBeast: 666})  //false

这是有原因的,因为可能有,例如 private variables within objects.

但是,如果您只是使用对象结构来包含数据,则仍然可以进行比较:

Object.prototype.equals = function(object2) {
    //For the first loop, we only check for types
    for (propName in this) {
        //Check for inherited methods and properties - like .equals itself
        //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
        //Return false if the return value is different
        if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
            return false;
        }
        //Check instance type
        else if (typeof this[propName] != typeof object2[propName]) {
            //Different types => not equal
            return false;
        }
    }
    //Now a deeper check using other objects property names
    for(propName in object2) {
        //We must check instances anyway, there may be a property that only exists in object2
            //I wonder, if remembering the checked values from the first loop would be faster or not 
        if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
            return false;
        }
        else if (typeof this[propName] != typeof object2[propName]) {
            return false;
        }
        //If the property is inherited, do not check any more (it must be equa if both objects inherit it)
        if(!this.hasOwnProperty(propName))
          continue;
        
        //Now the detail check and recursion
        
        //This returns the script back to the array comparing
        /**REQUIRES Array.equals**/
        if (this[propName] instanceof Array && object2[propName] instanceof Array) {
                   // recurse into the nested arrays
           if (!this[propName].equals(object2[propName]))
                        return false;
        }
        else if (this[propName] instanceof Object && object2[propName] instanceof Object) {
                   // recurse into another objects
                   //console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
           if (!this[propName].equals(object2[propName]))
                        return false;
        }
        //Normal value comparison for strings and numbers
        else if(this[propName] != object2[propName]) {
           return false;
        }
    }
    //If everything passed, let's say YES
    return true;
}  

但是,请记住,这是用于比较 JSON 之类的数据,而不是类实例和其他内容。如果您想比较更复杂的对象,请查看 this answer and it's super long function
要使其与 Array.equals 一起使用,您必须稍微编辑原始函数:

...
    // Check if we have nested arrays
    if (this[i] instanceof Array && array[i] instanceof Array) {
        // recurse into the nested arrays
        if (!this[i].equals(array[i]))
            return false;
    }
    /**REQUIRES OBJECT COMPARE**/
    else if (this[i] instanceof Object && array[i] instanceof Object) {
        // recurse into another objects
        //console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
        if (!this[i].equals(array[i]))
            return false;
        }
    else if (this[i] != array[i]) {
...

我做了一个little test tool for both of the functions

奖励:带有 indexOf 并包含的嵌套数组

Samy Bencherif has prepared 适用于您在嵌套数组中搜索特定对象的有用函数,可在此处获得:https://jsfiddle.net/SamyBencherif/8352y6yw/


如果要进行严格比较,请使用 this[i] !== array[i] 而不是 !=
您的方法应称为 equals 而不是 compare。至少在 .NET 中,比较通常返回一个带符号的 int,指示哪个对象大于另一个。请参阅:Comparer.Compare
这不仅是正确的做法,而且效率也相当高。这是我为这个问题中建议的所有方法准备的快速 jsperf 脚本。 jsperf.com/comparing-arrays2
更改内置类型的原型绝对不是正确的方法
此外,这与重写是否容易无关,而是答案不应该推荐被认为是不好的做法 (developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/…) 并且应该绝对不要在标题“The正确的路”
d
dippas

虽然这只适用于标量数组(见下面的注释),但它是短代码:

array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})

与上述相同,但在 ECMAScript 6 / CoffeeScript / TypeScript 中带有箭头函数:

array1.length === array2.length && array1.every((value, index) => value === array2[index])

(注意:此处的“标量”表示可以使用 === 直接比较的值。因此:数字、字符串、引用对象、引用函数。有关比较运算符的更多信息,请参见 the MDN reference)。

更新

根据我在评论中读到的内容,对数组进行排序和比较可能会给出准确的结果:

const array2Sorted = array2.slice().sort();
array1.length === array2.length && array1.slice().sort().every(function(value, index) {
    return value === array2Sorted[index];
});

例如:

array1 = [2,3,1,4];
array2 = [1,2,3,4];

然后上面的代码将返回 true


我喜欢这个,尽管读者应该知道这仅适用于排序数组。
它适用于任何类型的数组,无论是否排序@espertus
对,就是这样。这个函数应该比较两个数组,不管它们是否排序,它们的连续元素必须相等。
@espertus确实,如果元素在两个数组中的顺序不同,它就不会返回true。但是,相等性检查的目标不是检查它们是否包含相同的元素,而是检查它们是否具有相同顺序的相同元素。
如果要检查两个数组是否相等,包含相同的未排序项(但未多次使用),可以使用 a1.length==a2.length && a1.every((v,i)=>a2.includes(v))var a1 =[1,2,3], a2 = [3,2,1];var a1 =[1,3,3], a2 = [1,1,3]; 不会按预期工作)
B
Beau Smith

我喜欢将 Underscore 库用于数组/对象繁重的编码项目......在 Underscore 和 Lodash 中,无论您是在比较数组还是对象,它看起来都像这样:

_.isEqual(array1, array2)   // returns a boolean
_.isEqual(object1, object2) // returns a boolean

下划线 isEqual 文档

Lodash isEqual 文档


请注意,顺序很重要_.isEqual([1,2,3], [2,1,3]) => false
或者如果您只想要 isEqual 功能,您可以随时使用 lodash.isequal 模块
您也许可以使用 _.difference();如果订单对您不重要
如果顺序无关紧要,我们可以在此检查之前对数组进行排序_.isEqual([1,2,3].sort(), [2,1,3].sort()) => true
在 React.js 中抛出异常:'_' is not defined
r
radtek

我认为这是使用 JSON stringify 最简单的方法,在某些情况下它可能是最好的解决方案:

JSON.stringify(a1) === JSON.stringify(a2);

这会将对象 a1a2 转换为字符串,以便可以比较它们。在大多数情况下,顺序很重要,因为它可以使用上述答案之一中显示的排序算法对对象进行排序。

请注意,您不再比较对象,而是对象的字符串表示形式。它可能不是你想要的。


@PardeepJain,这是因为默认情况下,ECMAScript for Objects 中的相等运算符在引用相同的内存位置时返回 true。试试 var x = y = []; // 现在相等返回 true。
只是要注意 JSON stringify 函数并不快。与更大的数组一起使用肯定会引入延迟。
该问题专门询问是否有比使用 JSON.stringify 更好/更快的方法。
它更详细地说明了为什么在某些情况下这可能是一个很好的解决方案。
老实说,起初我没有注意到原始问题提到 JSON.stringify() - 对于简单的用例,它感觉是最简单的......
C
Community

本着原始问题的精神:

我想比较两个数组......理想情况下,有效。没有什么花哨的,如果它们相同则为真,否则为假。

我一直在使用以下 results(从快到慢)对此处提出的一些更简单的建议进行性能测试:

while (67%) 作者:Tim Down

var i = a1.length;
while (i--) {
    if (a1[i] !== a2[i]) return false;
}
return true

every (69%) by user2782196

a1.every((v,i)=> v === a2[i]);

reduce (74%) 按 DEI

a1.reduce((a, b) => a && a2.includes(b), true);

join & toString (78%),Gaizka Allende &维维克

a1.join('') === a2.join('');

a1.toString() === a2.toString();

half toString (90%) 作者:Victor Palomo

a1 == a2.toString();

stringify (100%) by radtek

JSON.stringify(a1) === JSON.stringify(a2);

请注意,下面的示例假定数组是已排序的一维数组。 .length 比较已针对通用基准删除(将 a1.length === a2.length 添加到任何建议中,您将获得约 10% 的性能提升)。选择最适合您的解决方案,了解每种解决方案的速度和限制。不相关的注释:有趣的是,看到人们对这个问题的完全合法的答案在否决按钮上得到所有触发快乐的约翰韦恩斯。


该链接会打开一个空测试。
如果您增加数组大小,则这些数字不适用(尤其是 reduce 方法)。试试 Array.from({length: 1000}).map((a,v)=> ${v}.padStart(10,2));
应该在 a1 之前使用 sort() & a2 加入。例如a1.sort().join("")===a2.sort().join("")
感谢您的性能测试:)
join('')['foo', 'bar'].join('') == ['foobar'].join('') 一样危险。我更喜欢a1 ==''+ a2
M
Mulan

实用之道

如果与“错误”解决方案相比,它只是“正确”(“正确”),那么说特定实现是“正确的方式™”是错误的。 Tomáš 的解决方案是对基于字符串的数组比较的明显改进,但这并不意味着它在客观上是“正确的”。到底什么是对的?它是最快的吗?它是最灵活的吗?是不是最容易理解?是不是调试最快?它使用最少的操作吗?它有任何副作用吗?没有一种解决方案可以拥有最好的一切。

Tomáš 可以说他的解决方案很快,但我也想说它是不必要的复杂。它试图成为一个适用于所有数组的一体化解决方案,无论是否嵌套。事实上,它甚至接受的不仅仅是数组作为输入,而且仍然试图给出一个“有效”的答案。

泛型提供可重用性

我的回答会以不同的方式处理这个问题。我将从一个仅涉及遍历数组的通用 arrayCompare 过程开始。从那里,我们将构建其他基本比较函数,如 arrayEqualarrayDeepEqual

// arrayCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayCompare = f => ([x,...xs]) => ([y,...ys]) =>
  x === undefined && y === undefined
    ? true
    : Boolean (f (x) (y)) && arrayCompare (f) (xs) (ys)

在我看来,最好的代码甚至不需要注释,这也不例外。这里几乎没有发生任何事情,您几乎可以毫不费力地理解此过程的行为。当然,有些 ES6 语法现在对你来说可能看起来很陌生,但这只是因为 ES6 相对较新。

正如类型所暗示的,arrayCompare 采用比较函数 f 和两个输入数组 xsys。在大多数情况下,我们所做的只是为输入数组中的每个元素调用 f (x) (y)。如果用户定义的 f 返回 false,我们会返回一个早期的 false——这要归功于 && 的短路评估。所以是的,这意味着比较器可以提前停止迭代,并防止在不必要时循环遍历输入数组的其余部分。

严格比较

接下来,使用我们的 arrayCompare 函数,我们可以轻松地创建我们可能需要的其他函数。我们将从基本的 arrayEqual ...

// equal :: a -> a -> Bool
const equal = x => y =>
  x === y // notice: triple equal

// arrayEqual :: [a] -> [a] -> Bool
const arrayEqual =
  arrayCompare (equal)

const xs = [1,2,3]
const ys = [1,2,3]
console.log (arrayEqual (xs) (ys))      //=> true
// (1 === 1) && (2 === 2) && (3 === 3)  //=> true

const zs = ['1','2','3']
console.log (arrayEqual (xs) (zs))      //=> false
// (1 === '1')                          //=> false

就那么简单。 arrayEqual 可以用 arrayCompare 和一个比较器函数定义,该函数使用 ===ab 进行比较(严格相等)。

请注意,我们还将 equal 定义为它自己的函数。这突出了 arrayCompare 作为高阶函数的作用,以在另一种数据类型(数组)的上下文中利用我们的一阶比较器。

松散比较

我们可以使用 == 来轻松定义 arrayLooseEqual。现在当比较 1(数字)和 '1'(字符串)时,结果将是 true ...

// looseEqual :: a -> a -> Bool
const looseEqual = x => y =>
  x == y // notice: double equal

// arrayLooseEqual :: [a] -> [a] -> Bool
const arrayLooseEqual =
  arrayCompare (looseEqual)

const xs = [1,2,3]
const ys = ['1','2','3']
console.log (arrayLooseEqual (xs) (ys))    //=> true
// (1 == '1') && (2 == '2') && (3 == '3')  //=> true

深度比较(递归)

您可能已经注意到,这只是肤浅的比较。当然,Tomáš 的解决方案是“The Right Way™”,因为它进行了隐含的深度比较,对吧?

好吧,我们的 arrayCompare 程序用途广泛,足以让深度相等测试变得轻而易举……

// isArray :: a -> Bool
const isArray =
  Array.isArray

// arrayDeepCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayDeepCompare = f =>
  arrayCompare (a => b =>
    isArray (a) && isArray (b)
      ? arrayDeepCompare (f) (a) (b)
      : f (a) (b))

const xs = [1,[2,[3]]]
const ys = [1,[2,['3']]]
console.log (arrayDeepCompare (equal) (xs) (ys)) //=> false
// (1 === 1) && (2 === 2) && (3 === '3')         //=> false

console.log (arrayDeepCompare (looseEqual) (xs) (ys)) //=> true
// (1 == 1) && (2 == 2) && (3 == '3')                 //=> true

就那么简单。我们使用 another 高阶函数构建了一个深度比较器。这次我们使用自定义比较器包装 arrayCompare,该比较器将检查 ab 是否为数组。如果是,则重新应用 arrayDeepCompare,否则将 ab 与用户指定的比较器 (f) 进行比较。这使我们能够将深度比较行为与我们实际比较单个元素的方式分开。即,如上例所示,我们可以使用 equallooseEqual 或我们制作的任何其他比较器进行深度比较。

因为 arrayDeepCompare 是柯里化的,所以我们也可以像在前面的示例中那样部分应用它

// arrayDeepEqual :: [a] -> [a] -> Bool
const arrayDeepEqual =
  arrayDeepCompare (equal)

// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
  arrayDeepCompare (looseEqual)

对我来说,这已经是对 Tomáš 解决方案的明显改进,因为我可以根据需要为我的数组明确选择浅层或深层比较。

对象比较(示例)

现在,如果您有一组对象或其他东西怎么办?如果每个对象具有相同的 id 值,也许您想将这些数组视为“相等”......

// idEqual :: {id: Number} -> {id: Number} -> Bool
const idEqual = x => y =>
  x.id !== undefined && x.id === y.id

// arrayIdEqual :: [a] -> [a] -> Bool
const arrayIdEqual =
  arrayCompare (idEqual)

const xs = [{id:1}, {id:2}]
const ys = [{id:1}, {id:2}]
console.log (arrayIdEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2)            //=> true

const zs = [{id:1}, {id:6}]
console.log (arrayIdEqual (xs) (zs)) //=> false
// (1 === 1) && (2 === 6)            //=> false

就那么简单。这里我使用了 vanilla JS 对象,但这种类型的比较器可以适用于任何对象类型;甚至您的自定义对象。 Tomáš 的解决方案需要完全重新设计以支持这种平等测试

带有对象的深数组?不是问题。我们构建了高度通用的通用函数,因此它们可以在各种用例中工作。

const xs = [{id:1}, [{id:2}]]
const ys = [{id:1}, [{id:2}]]
console.log (arrayCompare (idEqual) (xs) (ys))     //=> false
console.log (arrayDeepCompare (idEqual) (xs) (ys)) //=> true

任意比较(示例)

或者如果你想做一些其他类型的完全任意的比较怎么办?也许我想知道每个 x 是否大于每个 y ...

// gt :: Number -> Number -> Bool
const gt = x => y =>
  x > y

// arrayGt :: [a] -> [a] -> Bool
const arrayGt = arrayCompare (gt)

const xs = [5,10,20]
const ys = [2,4,8]
console.log (arrayGt (xs) (ys))     //=> true
// (5 > 2) && (10 > 4) && (20 > 8)  //=> true

const zs = [6,12,24]
console.log (arrayGt (xs) (zs))     //=> false
// (5 > 6)                          //=> false

少即是多

你可以看到我们实际上用更少的代码做更多的事情。 arrayCompare 本身并不复杂,我们制作的每个自定义比较器都有一个非常简单的实现。

轻松地,我们可以准确地定义我们希望如何比较两个数组 - 浅、深、严格、松散、一些对象属性或一些任意计算,或这些的任意组合 - 全部使用一个过程arrayCompare。甚至可以设想一个 RegExp 比较器!我知道孩子们多么喜欢那些正则表达式……

它是最快的吗?没有。但它可能也不需要。如果速度是衡量我们代码质量的唯一指标,那么很多非常好的代码都会被丢弃——这就是为什么我将这种方法称为实用方法。或者更公平地说,一种实用的方法。此描述适用于此答案,因为我并不是说此答案仅与其他答案相比才实用;这是客观事实。我们以非常容易推理的极少代码实现了高度的实用性。没有其他代码可以说我们没有得到这个描述。

这是否使它成为您的“正确”解决方案?这由你决定。没有其他人可以为您做到这一点;只有你知道你的需求是什么。在几乎所有情况下,我更看重简单、实用和通用的代码,而不是聪明和快速的代码。你看重的可能会有所不同,所以选择适合你的。

编辑

我的旧答案更侧重于将 arrayEqual 分解为小程序。这是一个有趣的练习,但并不是解决这个问题的最佳(最实用)方法。如果您有兴趣,可以查看此修订历史记录。


“最好的代码甚至不需要注释”......不想这么说,但这段代码可以使用更多的注释和/或不同的名称——“比较”是相当模糊的。如果我没看错,你的“比较”本质上是一个咖喱递归的“每个”。我认为。或者它是一个咖喱递归“一些”?唔。这需要更多的思考而不是必要的。也许更好的名字是“arraysEquivalent”,利用“等价关系”的标准术语。或者,更清楚(无论如何对我来说)“递归等效”。
@DonHatch 感谢您有机会回复。 “比较”是指 arrayCompare 吗?是的,该函数是 curried,但它不同于 someeveryarrayCompare 采用比较器和 两个 数组进行比较。我选择了一个特别通用的名称,因为我们可以使用任意函数来比较数组。该函数是柯里化的,因此可以专门用于创建新的数组比较函数(例如,arrayEqual)。你能推荐一个更好的名字吗?您认为哪些方面需要额外的评论或解释?很高兴讨论^_^
不确定我的观点是否清楚 - 但我的观点是,你的函数并不是真的打算采用 arbitrary 函数,我不认为 - 它打算采用 equivalence relation ,它返回一个等价关系。这很重要——如果给定一些其他类型的任意二进制函数,比如我提到的那些,即使是人们经常称之为“比较”的函数,它也不会做任何明智的事情(我不认为)。因此,我认为将“等效”放在名称中代替“比较”会很有帮助。
@ftor,作者:超级有用的答案,干得好,+1。反馈:您提倡简单,但对于许多开发人员来说,一行三个箭头的表达方式并不简单或易于理解。例如:f=> ([x,...xs]) => ([y,...ys]) =>。我经常使用它,但仍然不得不在精神上分解它,而不是“只看它”。第二点是正确的,使用every。即使权衡您的理由,总的来说,在尝试推断您的设计理念时,不仅对我而言,而且从您的角度来看,这似乎更好。
我知道这是一个学习的地方,但我在这里假设学习函数式风格的普通程序员可以将任何 curried 函数转换为 uncurried 函数。我的回答并没有暗示这种风格是要在你自己的程序中使用的——不加咖喱的,用你自己的缩进规则写,随心所欲地写——我以我相信表达的风格写我的答案节目最好。我也喜欢邀请其他人挑战我们在语法上表达程序的方式
T
Tim Down

目前尚不清楚您所说的“相同”是什么意思。例如,下面的数组 ab 是否相同(注意嵌套数组)?

var a = ["foo", ["bar"]], b = ["foo", ["bar"]];

这是一个优化的数组比较函数,它使用严格相等依次比较每个数组的对应元素,并且不对本身是数组的数组元素进行递归比较,这意味着对于上面的示例,arraysIdentical(a, b) 将返回 false。它适用于一般情况,而基于 JSON 和 join() 的解决方案不会:

function arraysIdentical(a, b) {
    var i = a.length;
    if (i != b.length) return false;
    while (i--) {
        if (a[i] !== b[i]) return false;
    }
    return true;
};

@ASDF:从问题中不清楚“相同”是什么意思。显然,这个答案只是做了一个浅薄的检查。我会添加一个注释。
这对于arraysIdentical([1, 2, [3, 2]],[1, 2, [3, 2]]) 失败;
@GopinathShiva:好吧,只有当您期望它返回 true 时它才会失败。答案说明它不会。如果您需要比较嵌套数组,您可以轻松添加递归检查。
E
Evan Steinkerchner

根据 Tomáš Zato 的回答,我同意仅遍历数组是最快的。此外(就像其他人已经说过的那样),该函数应该被称为等于/等于,而不是比较。鉴于此,我修改了函数来处理比较数组的相似性——即它们具有相同的元素,但顺序混乱——供个人使用,我想我会把它放在这里给大家看看。

Array.prototype.equals = function (array, strict) {
    if (!array)
        return false;

    if (arguments.length == 1)
        strict = true;

    if (this.length != array.length)
        return false;

    for (var i = 0; i < this.length; i++) {
        if (this[i] instanceof Array && array[i] instanceof Array) {
            if (!this[i].equals(array[i], strict))
                return false;
        }
        else if (strict && this[i] != array[i]) {
            return false;
        }
        else if (!strict) {
            return this.sort().equals(array.sort(), true);
        }
    }
    return true;
}

此函数采用默认为 true 的附加参数 strict。这个严格的参数定义了数组是否需要在内容和这些内容的顺序上完全相等,或者只是包含相同的内容。

例子:

var arr1 = [1, 2, 3, 4];
var arr2 = [2, 1, 4, 3];  // Loosely equal to 1
var arr3 = [2, 2, 3, 4];  // Not equal to 1
var arr4 = [1, 2, 3, 4];  // Strictly equal to 1

arr1.equals(arr2);         // false
arr1.equals(arr2, false);  // true
arr1.equals(arr3);         // false
arr1.equals(arr3, false);  // false
arr1.equals(arr4);         // true
arr1.equals(arr4, false);  // true

我还用这个函数和这个例子写了一个快速的 jsfiddle:
http://jsfiddle.net/Roundaround/DLkxX/


e
epascarello

与 JSON.encode 相同的是使用 join()。

function checkArrays( arrA, arrB ){

    //check if lengths are different
    if(arrA.length !== arrB.length) return false;


    //slice so we do not effect the original
    //sort makes sure they are in order
    //join makes it a string so we can do a string compare
    var cA = arrA.slice().sort().join(","); 
    var cB = arrB.slice().sort().join(",");

    return cA===cB;

}

var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];  //will return true

console.log( checkArrays(a,b) );  //true
console.log( checkArrays(a,c) );  //false
console.log( checkArrays(a,d) );  //false
console.log( checkArrays(a,e) );  //true

唯一的问题是您是否关心最后比较测试的类型。如果您关心类型,则必须循环。

function checkArrays( arrA, arrB ){

    //check if lengths are different
    if(arrA.length !== arrB.length) return false;

    //slice so we do not effect the orginal
    //sort makes sure they are in order
    var cA = arrA.slice().sort(); 
    var cB = arrB.slice().sort();

    for(var i=0;i<cA.length;i++){
         if(cA[i]!==cB[i]) return false;
    }

    return true;

}

var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];

console.log( checkArrays(a,b) );  //true
console.log( checkArrays(a,c) );  //false
console.log( checkArrays(a,d) );  //false
console.log( checkArrays(a,e) );  //false

如果顺序应该保持不变,而不是只是一个循环,则不需要排序。

function checkArrays( arrA, arrB ){

    //check if lengths are different
    if(arrA.length !== arrB.length) return false;


    for(var i=0;i<arrA.length;i++){
         if(arrA[i]!==arrB[i]) return false;
    }

    return true;

}

var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];

console.log( checkArrays(a,a) );  //true
console.log( checkArrays(a,b) );  //false
console.log( checkArrays(a,c) );  //false
console.log( checkArrays(a,d) );  //false
console.log( checkArrays(a,e) );  //false

这仅适用于某些阵列,并且对于大型阵列会非常慢。
生成 JSON 也是循环的,你只是(或者看起来如此)不知道它。除了循环之外,生成 JSON 还需要更多内存 - 它在比较之前创建所述数组的 2 个字符串表示形式。实现了下注功能以从最好到最差的顺序排列答案。我认为你的答案不是一个好的答案,所以我投了反对票。
抱歉,我刚才说的是 JSON 而不是 .join()。也许如果您将第二个解决方案声明为主要解决方案(因为它是更好的解决方案,尽管对多维数组没有影响),我不会那样判断您。到目前为止,我拒绝了所有将数组转换为字符串的答案。同样,我赞成所有使用正确方式的人,以防你需要知道。这意味着@Tim Down 的回答和 Bireys 的回答。
第一个版本失败:checkArrays([1,2,3] , ["1,2",3]) == true,这不太可能是您想要发生的事情!
@epascarello:是的,您可以,但是(除了您建议的非常长的分隔符效率低下)这意味着存在一些边缘情况(数组恰好包含一个带有您的分隔符的字符串)其中 checkArrays() 函数行为不端.如果您对数组的内容有所了解,这可能不是问题(因此您可以选择一个肯定不会出现在数组项中的分隔符),但是如果您尝试编写 general 数组比较函数,然后像这样使用 join() 会产生微妙的错误!
y
yesnik

在我的例子中,比较数组只包含数字和字符串。此函数将显示数组是否包含相同的元素。

function are_arrs_match(arr1, arr2){
    return arr1.sort().toString() === arr2.sort().toString()
}

让我们测试一下!

arr1 = [1, 2, 3, 'nik']
arr2 = ['nik', 3, 1, 2]
arr3 = [1, 2, 5]

console.log (are_arrs_match(arr1, arr2)) //true
console.log (are_arrs_match(arr1, arr3)) //false

该问题不要求您进行排序,因此对于 are_arrs_equal([1,2], [2,1]) 等示例,您的解决方案是错误的。此外,请参阅此页面上的其他讨论,了解为什么字符串化是不必要、脆弱和错误的。
are_arrs_equal([1,2], [2,1]) 按预期返回 true。也许这个解决方案并不理想,但它对我有用。
这正是问题所在,对于有序数据结构,这两者在任何理智意义上的“相等”一词都不相等。它们是数组,而不是集合,如果你想要集合相等,你应该这样称呼它——并回答一个不同的问题。 :-)
我同意上面的评论,但这个解决方案也适用于我的简单整数数组,其中顺序并不重要,所以我会使用它。
are_arrs_match([1,2], ["1,2"]) 失败(返回 true)。请注意,the sort() 调用将修改输入数组 - 这可能是不可取的。
J
Jeferson Euclides

尽管这有很多答案,但我认为这会有所帮助:

const newArray = [ ...new Set( [...arr1, ...arr2] ) ]

问题中没有说明数组的结构将是什么样子,所以如果你确定你的数组中没有嵌套数组或对象(它发生在我身上,这就是我来这个的原因回答)上面的代码将起作用。

发生的情况是我们使用扩展运算符 ( ... ) 来连接两个数组,然后我们使用 Set 来消除任何重复。一旦你有了它,你就可以比较它们的大小,如果所有三个数组都具有相同的大小,那么你就可以开始了。

正如我所说,这个答案也忽略了元素的顺序,确切的情况发生在我身上,所以也许处于相同情况的人可能会在这里结束(就像我一样)。

编辑1。

回答 Dmitry Grinko 的问题:“你为什么在这里使用扩展运算符 ( ... ) - ...new Set ?它不起作用”

考虑这段代码:

const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ new Set( [...arr1, ...arr2] ) ]
console.log(newArray)

你会得到

[ Set { 'a', 'b', 'c' } ]

为了使用该值,您需要使用一些 Set 属性(请参阅 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)。另一方面,当您使用此代码时:

const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ ...new Set( [...arr1, ...arr2] ) ]
console.log(newArray)

你会得到

[ 'a', 'b', 'c' ]

这就是区别,前者会给我一个 Set,它也可以工作,因为我可以获得那个 Set 的大小,但后者给了我我需要的数组,更直接的分辨率。


为什么在这里使用扩展运算符 ( ... ) - ...new Set ?它不起作用。
Dmitry Grinko 我相信我在 Edit1 上回答了您的问题。但我不确定你说“它不起作用”是什么意思,因为这两个答案都会妨碍你
[1, 2, 2, 3], [1, 2, 2, 3] < 这不是简单地失败了吗?结果集的长度与输入不同。
@LeoLei 你是对的,正如帖子中所解释的,这对于某些情况非常具体。我在比较数据库响应时使用了它,我知道我不会遇到与您提到的情况类似的情况。
K
Kamil Kiełczewski

最短的

对于一组数字,请尝试:

a1==''+a2

var a1 = [1,2,3]; var a2 = [1,2,3];控制台日志(a1==''+a2)

注意:当数组还包含字符串时,此方法将不起作用,例如 a2 = [1, "2,3"]


D
Durga prasad patra

当两个数组具有相同元素但顺序不同时,您的代码将无法正确处理这种情况。

用您的示例查看我的代码,该示例比较两个元素为数字的数组,您可以针对其他元素类型修改或扩展它(通过使用 .join() 而不是 .toString())。

var a1 = [1,2,3]; var a2 = [1,2,3]; const arraysAreEqual = a1.sort().toString()==a2.sort().toString(); // 如果两个数组具有相同的元素,则为 true 否则为 false console.log(arraysAreEqual);


H
Hamza Hmem

您可以简单地使用 lodash 库中的 isEqual。它非常高效和干净。

import {isEqual} from "lodash";

const isTwoArraysEqual = isEqual(array1, array2);

使用 lodash 比较数组非常简单。
@HadidAli 是的,在编程中,您不应该构建已经由其他开发人员或团队构建的东西,或者存在于像 Lodash 这样的轻量级库中
使用 Lodash isEqual 和 sortBy: isEqual(sortBy(array1), sortBy(array2));
s
swift-lynx

代码打高尔夫球

有很多答案展示了如何有效地比较数组。

下面是比较两个 int 或(字符串)数组的最短方法,以代码字节为单位。

const a = [1, 2, 3] const b = [1, 2, 3] console.log("1.", a.join() == b.join()) console.log("2." , a.join() == [].join()) console.log("3.", 1 + a == 1 + b) console.log("4.", 1 + [] == 1 + b) // 更短的 console.log("4.b) ", a == "" + b) // 误报(见缺陷) console.log("5. ", 1 + ["3"] = = 1 + [3]) // 类型差异 console.log("6.", 1 + ["1,2"] == 1 + ["1", "2"])

解释

这是因为当使用 + 运算符时,类型会自动转换为允许连接。在这种情况下,1[1, 2, 3] 都被转换为字符串。

在内部,JavaScript 使用 [1, 2, 3].join() 将数组转换为字符串,然后将它们添加到 11,2,3。在两个数组上执行此操作时,可以简单地使用 ===== 来比较两个字符串。

缺陷

使用这种技术,比较不关心要比较的数组中的元素是否属于不同类型。由于字符串转换,[1, 2] 将等于 ["1", "2"]

编辑:正如评论中所指出的,比较字符串数组可能会产生误报,例如 ["1,2"]["1", "2"] '相等'。如果您确定这些永远不会发生(例如在许多代码高尔夫挑战中),则无需担心。

免责声明

虽然这对于代码打高尔夫球很有用,但它可能不应该在生产代码中使用。指出的两个缺陷也无济于事。


啊啊啊。简单。
它适用于数字,但不适用于字符串数组,例如 1+["1","2,3"]===1+["1,2","3"]["1","2,3"].join()===["1,2","3"].join()(因此您可以表明您的答案仅适用于数字)
我什至没有考虑过……如果担心这种边缘情况,我的解决方案将行不通。我更新了我的答案以清楚地说明这个缺陷。
m
michaels234

这里有很多复杂的长答案,所以我只想提供一个非常简单的答案:使用 toString() 将数组转换为简单的逗号分隔字符串,您可以轻松地与 === 进行比较

let a = [1, 2, 3]
let b = [1, 2, 3]
let c = [4, 2, 3]

console.log(a.toString())  // this outputs "1,2,3"
console.log(a.toString() === b.toString())  // this outputs true because "1,2,3" === "1,2,3"
console.log(a.toString() === c.toString())  // this outputs false because "1,2,3" != "4,2,3"

在第二个数组具有相同值但索引不同的情况下,这不是一个好方法。 1,2,3 !== 3,2,1。所以也许你需要先对数组进行排序。
我想知道当元素的顺序不同时,有人如何投票这个答案,它不起作用
因为通常元素的顺序很重要,并且具有不同元素顺序的 2 个不同的数组是不一样的。您不能相同地使用它们或使用相同的索引等访问它们。
E
Esqarrouth

这是一个打字稿版本:

//https://stackoverflow.com/a/16436975/2589276
export function arraysEqual<T>(a: Array<T>, b: Array<T>): boolean {
    if (a === b) return true
    if (a == null || b == null) return false
    if (a.length != b.length) return false

    for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i]) return false
    }
    return true
}

//https://stackoverflow.com/a/16436975/2589276
export function arraysDeepEqual<T>(a: Array<T>, b: Array<T>): boolean {
    return JSON.stringify(a) === JSON.stringify(b)
}

mocha 的一些测试用例:

it('arraysEqual', function () {
    let a = [1,2]
    let b = [1,2]
    let c = [2,3]
    let d = [2, 3]
    let e = ['car','apple','banana']
    let f = ['car','apple','banana']
    let g = ['car','apple','banan8']

    expect(arraysEqual(a, b)).to.equal(true)
    expect(arraysEqual(c, d)).to.equal(true)
    expect(arraysEqual(a, d)).to.equal(false)
    expect(arraysEqual(e, f)).to.equal(true)
    expect(arraysEqual(f, g)).to.equal(false)
})

it('arraysDeepEqual', function () {
    let a = [1,2]
    let b = [1,2]
    let c = [2,3]
    let d = [2, 3]
    let e = ['car','apple','banana']
    let f = ['car','apple','banana']
    let g = ['car','apple','banan8']
    let h = [[1,2],'apple','banan8']
    let i = [[1,2],'apple','banan8']
    let j = [[1,3],'apple','banan8']

    expect(arraysDeepEqual(a, b)).to.equal(true)
    expect(arraysDeepEqual(c, d)).to.equal(true)
    expect(arraysDeepEqual(a, d)).to.equal(false)
    expect(arraysDeepEqual(e, f)).to.equal(true)
    expect(arraysDeepEqual(f, g)).to.equal(false)
    expect(arraysDeepEqual(h, i)).to.equal(true)
    expect(arraysDeepEqual(h, j)).to.equal(false)
})

C
CertainPerformance

2020 年推出了 Stage 1 proposal,通过将 Array.prototype.equals 添加到语言中,可以轻松比较数组。这就是它的工作方式,无需任何库、monkeypatching 或任何其他代码:

[1, 2, 3].equals([1, 2, 3]) // evaluates to true
[1, 2, undefined].equals([1, 2, 3]) // evaluates to false
[1, [2, [3, 4]]].equals([1, [2, [3, 4]]]) // evaluates to true

到目前为止,这只是一个暂定提案 - TC39 will now“花时间研究问题空间、解决方案和横切关注点”。如果它进入第 2 阶段,它很有可能最终被集成到适当的语言中。


m
metakermit

如果您将 Mocha 之类的测试框架与 Chai 断言库一起使用,则可以使用 deep 相等性来比较数组。

expect(a1).to.deep.equal(a2)

仅当数组在相应索引处具有相等元素时,这才应返回 true。


G
Gaizka Allende

如果它们只是两个数字或字符串数组,这是一个快速的单行数组

const array1 = [1, 2, 3];
const array2 = [1, 3, 4];
console.log(array1.join(',') === array2.join(',')) //false

const array3 = [1, 2, 3];
const array4 = [1, 2, 3];
console.log(array3.join(',') === array4.join(',')) //true

常量数组1 = [1];常量数组2 = [1, 1]; console.log(array1.join('') === array2.join('')) //返回 true
它不应该:array1.join('') 是 '1' 而 array2.join('') 是 '11'
对不起,错字。第一个数组应该是 [11]。很明显为什么会发生这种情况以及如何解决。
不知道你在说什么,这很简单: [1].join() 是“1”而 [1,1].join() 是“1,1”,所以它们永远不会相等
请再仔细阅读我的评论。如果您仍然没有看到它,请在 ideone.com/KFu427 抢夺
t
try-catch-finally

另一种代码很少的方法(使用 Array reduceArray includes):

arr1.length == arr2.length && arr1.reduce((a, b) => a && arr2.includes(b), true)

如果您还想比较顺序的相等性:

arr1.length == arr2.length && arr1.reduce((a, b, i) => a && arr2[i], true)

长度检查确保一个数组中的元素集不仅仅是另一个数组的子集。

reducer 用于遍历一个数组并搜索另一个数组中的每一项。如果没有找到一项,reduce 函数将返回 false。在第一个示例中,正在测试包含一个元素第二个示例也检查订单

在第一个示例中,正在测试包含一个元素

第二个示例也检查订单


如果数组的元素之一为 0,则将始终失败。
A
Ankit Kumar

这里有很多好的答案。我通常是这样做的——

if ( arr1.length === arr2.length && arr1.every((a1) => arr2.includes(a1)) ) {
   // logic
}

如果所有元素都通过给定的camparison 逻辑,every() 只会返回 true。如果遇到 false,在任何迭代中,它都会终止并返回 false。时间复杂度为 O(n*m)。


A
Aditya

干得好,

const a = [1, 2, 3] const b = [1, 2, 3, 4, 5] const diff = b.filter(e => !a.includes(e)) console.log(diff)

上述大多数答案都不适用于无序列表。这也适用于无序列表。

const a = [3, 2, 1] const b = [1, 2, 3, 4, 5] const diff = b.filter(e => !a.includes(e)) console.log(diff)

如果a的大小大于b,

const a = [1, 2, 3, 4, 5] const b = [3, 2, 1] const diff = a.length > b.length ? a.filter(e => !b.includes(e)) : b.filter(e => !a.includes(e)) console.log(diff)


它不适用于: let a = [1, 2, 3] let b = [3, 2, 3]
上述解决方案尝试在 [1,2,3] 中查找 b [3,2,3] 中不存在的数字,对于您的情况,b (2,3) 中的所有唯一数字都存在于a (1,2,3),这就是它打印一个空数组的原因。
@AkshayVijayJain,您可以比较数组的长度, const diff = a.length > b.length 吗? a.filter(e => !b.includes(e)) : b.filter(e => !a.includes(e))
p
peonicles

我们可以使用 every (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/every) 以函数方式执行此操作

function compareArrays(array1, array2) {
    if (array1.length === array2.length)
        return array1.every((a, index) => a === array2[index])
    else
        return false
}

// test
var a1 = [1,2,3];
var a2 = [1,2,3];

var a3 = ['a', 'r', 'r', 'a', 'y', '1']
var a4 = ['a', 'r', 'r', 'a', 'y', '2']

console.log(compareArrays(a1,a2)) // true
console.log(compareArrays(a1,a3)) // false
console.log(compareArrays(a3,a4)) // false

如果我们想要 arr1 == arr2 如果 arr1 的所有数据都在 arr2 中,则不起作用,反之亦然,以什么顺序无关紧要。
N
Nathan Boolean Trujillo

这比较了 2 个未排序的数组:

function areEqual(a, b) {
  if ( a.length != b.length) {
    return false;
  }
  return a.filter(function(i) {
    return !b.includes(i);
  }).length === 0;  
}

虽然昂贵(就计算资源而言),但这是一个健壮的解决方案,应该适用于各种类型,并且不依赖于排序!
这将无法比较 [1, 1, 2] 和 [2, 2, 1]
P
Pedro Rodrigues

一个简单的方法:

function equals(a, b) {
    if ((a && !b) || (!a && b) || (!a && !b) || (a.length !== b.length)) {
        return false;
    }

    var isDifferent = a.some(function (element, index) { 
        return element !== b[index];
    });

    return !isDifferent;
}

J
Jöcker

这是未排序数组和自定义比较的可能性:

    const array1 = [1,3,2,4,5];
    const array2 = [1,3,2,4,5];
    
    const isInArray1 = array1.every(item => array2.find(item2 => item===item2))
    const isInArray2 = array2.every(item => array1.find(item2 => item===item2))
    
    const isSameArray = array1.length === array2.length && isInArray1 && isInArray2
    
    console.log(isSameArray); //true

H
Harry

这是我的解决方案:

/**
 * Tests two data structures for equality
 * @param {object} x
 * @param {object} y
 * @returns {boolean}
 */
var equal = function(x, y) {
    if (typeof x !== typeof y) return false;
    if (x instanceof Array && y instanceof Array && x.length !== y.length) return false;
    if (typeof x === 'object') {
        for (var p in x) if (x.hasOwnProperty(p)) {
            if (typeof x[p] === 'function' && typeof y[p] === 'function') continue;
            if (x[p] instanceof Array && y[p] instanceof Array && x[p].length !== y[p].length) return false;
            if (typeof x[p] !== typeof y[p]) return false;
            if (typeof x[p] === 'object' && typeof y[p] === 'object') { if (!equal(x[p], y[p])) return false; } else
            if (x[p] !== y[p]) return false;
        }
    } else return x === y;
    return true;
};

适用于任何嵌套数据结构,并且显然忽略了对象的方法。甚至不要考虑用这种方法扩展 Object.prototype,当我尝试过一次时,jQuery 坏了;)

对于大多数数组,它仍然比大多数序列化解决方案更快。这可能是对象记录数组的最快比较方法。


不好!这些给出了 true:equal({}, {a:1})equal({}, null),而这个错误:equal({a:2}, null)
L
Leed
JSON.stringify(collectionNames).includes(JSON.stringify(sourceNames)) ?  array.push(collection[i]) : null

我就是这样做的。


很好的解决方案 - 但我想知道在某些情况下它是否不会总是按预期工作,例如使用某些原语或深度嵌套的数组?我希望它在所有情况下都有效
A
AL-zami

已经有一些很好的答案。但我想分享另一个想法,该想法已被证明在比较数组时是可靠的。我们可以使用 JSON.stringify ( ) 比较两个数组。它将从数组中创建一个字符串,从而比较从两个数组中获得的两个字符串是否相等

JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:1},2]) //true

JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},2]) //false

JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},[3,4],2]) //false

JSON.stringify([1,{a:1},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //false

JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //true

JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false

JSON.stringify([1,{a:2},[3,4,[4]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false

JSON.stringify([1,{a:2},[3,4,[5]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //true

不起作用是元素的顺序无关紧要。
你能详细说明一下吗?