如何在JavaScript中比较数组?

Jul*_*Lam 894 javascript arrays json

我想比较两个阵列......理想情况下,有效率.没有什么花哨的,只要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
Run Code Online (Sandbox Code Playgroud)

每个数组的JSON编码都有,但有没有更快或更"简单"的方法来简单地比较数组而不必迭代每个值?

Tom*_*ica 835

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

比较数组:

// 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});
Run Code Online (Sandbox Code Playgroud)

用法:

[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;
Run Code Online (Sandbox Code Playgroud)

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

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

比较对象:

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

({a:1, foo:"bar", numberOfTheBeast: 666}) == ({a:1, foo:"bar", numberOfTheBeast: 666})  //false
Run Code Online (Sandbox Code Playgroud)

这有一个原因,因为可能存在对象内的私有变量.

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

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;
}  
Run Code Online (Sandbox Code Playgroud)

但是,请记住,这个用于比较JSON之类的数据,而不是类实例和其他东西.如果你想比较mor复杂的对象,看看这个答案,它是超长函数.
要使这个工作,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]) {
...
Run Code Online (Sandbox Code Playgroud)

为这两个功能做了一个小测试工具.

额外:带indexOf和的嵌套数组contains

Samy Bencherif为您在嵌套数组中搜索特定对象的情况准备了有用的函数,可在此处获取:https://jsfiddle.net/SamyBencherif/8352y6yw/

  • 改变内置类型的原型绝对不是**正确的方式** (89认同)
  • 你的方法应该被称为`equals`而不是`compare`.至少在.NET中,compare通常会返回一个signed int来指示哪个对象大于另一个.请参阅:[Comparer.Compare](http://msdn.microsoft.com/en-us/library/2y07t0wt%28v=vs.110%29.aspx). (37认同)
  • 此外,它不是关于它是否容易重写,而是关于答案不应该推荐一些被认为是不良实践的事实(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/ Inheritance_and_the_prototype_chain#Bad_practice.3A_Extension_of_native_prototypes)并且应该**绝对**不要在标题"正确的方式"下面这样做 (29认同)
  • 如果你想进行严格的比较,请使用`this [i]!== array [i]`而不是`!=`. (26认同)
  • 这只是正确的做法,它也更有效率.这是我为这个问题中建议的所有方法准备的快速jsperf脚本.http://jsperf.com/comparing-arrays2 (15认同)
  • @TomášZato基本上,因为将来可能会在`Array`原型上有一个`equals()`函数,如果它不同(例如深度与浅对比,或者甚至只是添加一个参数以允许选择深或浅比较)您的代码突然开始破坏使用此功能的其他脚本... (11认同)
  • @TomášZato我同意你的意见,你可以在自己的代码中做任何你想做的事情.你可能不会遇到任何问题,但如果你这样做,你只会自己开枪.但是,我们正在谈论一个公共网站上的推荐,并且在那个网站上称自己是"正确的方式".同样的推理没有去,你不应该在脚下射击其他人. (7认同)
  • 为了在数组原型中编写函数,答案为+1,并为您的健康提供啤酒. (6认同)
  • 不,如果你********************************************这基本上只是polyfills的情况...... (5认同)
  • @TomášZato关于如何在腿上射击自己的建议不属于这个网站(除非问题是"我如何在腿上射击自己").而你的"我会更新答案"表明你仍然不明白这些问题.因为在那一点上,任何使用过这段代码的人都不会在这里看到你的更新,但却感到脚下的痛苦.两个人遵循这个策略怎么样?他们的代码不能在同一个站点上使用.你的答案可能有用(在某些情况下),但这并没有减少错误...... (5认同)
  • 很好的答案......但你不应该缓存循环的数组长度:`for(var i = 0,len = this.length; i <len; i ++)` (4认同)
  • @TomášZato是的,但是`Array`是.关键是,由于`Array`是内置的,你不应该修改它的原型. (4认同)
  • 感谢您的输入.考虑到它的观看次数,我想很多用户从谷歌重定向到这个难题的同样难以弄清楚如何"正确地"比较两个阵列! (3认同)
  • 实际上,将函数放在数组原型上可能不是正确的做法.与这样的全局对象混淆可能会导致您没有编写的代码中出现奇怪的问题 - 或者您执行的代码... (3认同)
  • @RobertSim否.这不能解决问题,只是表明你已经看到其他人这样做并复制了它们.想象一下,为数组定义一个等于标准的标准,它使用`===`来比较数组的所有成员.现在,使用您的函数的代码将无法正常工作(因为`Array.equals()`在执行此新函数的浏览器中执行了除预期之外的操作).在没有新功能的浏览器中,使用新标准功能的代码似乎可以正常工作,但不能达到预期效果. (3认同)
  • 你的`Array.ptototype.equals`应该使用严格的比较法.其他如"[[]].equals([false]);`将返回true. (3认同)
  • 通过将您的第一个条件更改为`if(!this ||!array)`,使此代码“严格模式”安全不是一个坏主意。这是一种罕见但可能的情况,因为this值设置为null或undefined,而在this.length上收到TypeError。 (2认同)
  • 我感到惊讶的是,没有本机函数可以比较数组,我真的需要复制粘贴代码才能使其工作吗?似乎应该在语言中包含一些内容。 (2认同)
  • 你永远不应该像这样循环数组。我记得以前学习javascript的时候,`i`也会包含`"length"`等默认属性。(在古代 IE 中) (2认同)
  • 我在这里制作了数组等于函数的非原型版本:http://stackoverflow.com/questions/27852356/finding-nested-duplicate-arrays-in-javascript-nested-array-uniq-in-lodash-unde/ 27859185#27859185 (2认同)
  • @TomášZato 啊。我没想到。我一直在研究短期项目以考虑警告。谢谢。:) (2认同)
  • 使用您的函数和新函数组合代码已变得不可能.添加polyfill会使事情变得更糟:现在,如果首先执行代码,则首先执行polyfill,而不是首先执行polyfill.即使标准中没有添加这样的功能,如果其他人决定和你做同样的事情,也会出现同样的问题,但是对于应该做什么等等有不同的看法......并且显示异常并不能解决任何问题.如果你不得不这样做,那将是一个很好的选择,但正如你自己所说,你没有必要; 你可以轻松地将其转换为正常功能. (2认同)
  • @TomášZato每当你拥有一个代码库时,我绝对会接受你所有的论点,特别是如果它像你说的那样短暂.如果您(1)包含*任何*第三方库(现在非常常见)和(2)代码仍然很少有机会继续使用ES7 +,那么您接受您的应用程序可能突然开始崩溃(或更糟糕的是,只要用户升级其运行时(浏览器,服务器端环境,......),就会显示无声但不正确的行为(*您可能永远不会知道*).我承认你有点恼火,你认为继承"过度杀戮",这正是你想要的. (2认同)
  • @tne Subclassed数组只能与`new ImprovedArray()`一起使用,而我的解决方案适用于实例化为`[]`的任何东西,无论它来自外部源,解析的JSON还是你的程序.我的回答已经存在了2年,仍然有效,并没有破坏任何东西(只会激怒一些我不关心的人).观察证据是唯一优于逻辑论证的东西(你所说的是合乎逻辑的和真实的).与此同时,任何关心他所写代码的人都会阅读你的意见,并根据你的建议做出决定. (2认同)
  • 这显然是一个非常热门的话题..我发现自己对js内置如何无法正确处理嵌套数组感到失望.因此,使用@TomášZato的代码,我创建了一个`contains`和`indexOf`函数,它与Zato的`equals`函数非常相似,可以正确处理嵌套数组.所有函数都添加到Array原型中.代码可在此处获取:https://jsfiddle.net/SamyBencherif/8352y6yw/ (2认同)

小智 348

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

array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})
Run Code Online (Sandbox Code Playgroud)

Rr,在带有箭头功能的ECMAScript 6/CoffeeScript/TypeScript中:

array1.length === array2.length && array1.every((value, index) => value === array2[index])
Run Code Online (Sandbox Code Playgroud)

(注意:'标量'在这里表示可以直接使用的值===.因此:数字,字符串,引用对象,引用函数.有关比较运算符的更多信息,请参阅MDN参考).

UPDATE

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

array1.length === array2.length && array1.sort().every(function(value, index) { return value === array2.sort()[index]});
Run Code Online (Sandbox Code Playgroud)

例如:

array1 = [2,3,1,4];
array2 = [1,2,3,4];
Run Code Online (Sandbox Code Playgroud)

然后上面的代码将给出 true

  • 对,就是这样.这个函数应该比较两个数组,无论它们是否排序都无关紧要,它们的连续元素必须相等. (35认同)
  • @espertus实际上,如果元素在两个数组中没有完全相同的顺序,它将不会返回true.但是,相等性检查的目的不是检查它们是否包含相同的元素,而是检查它们是否在相同的顺序中具有相同的元素. (22认同)
  • 我喜欢这个,虽然读者应该知道这只适用于排序数组. (17认同)
  • 它适用于任何类型的数组,排序或不排序@espertus (11认同)
  • 如果要检查两个数组是否相等,包含相同的未排序项(但多次不使用),可以使用`a1.length == a2.length && a1.every((v,i)=> a2.包括(v))`:`var a1 = [1,2,3],a2 = [3,2,1];`(`var a1 = [1,3,3],a2 = [1,1, 3];`不会按预期工作) (7认同)
  • 此外,调用 `sort()` 方法会改变原始数组,这几乎从来都不是一件好事。你应该事先克隆它。 (4认同)
  • 您不应该为第一个数组的每个元素对第二个数组进行排序。在函数执行开始时对它们进行排序。或者,更好的是,添加一个函数参数来确定是否需要排序。此外,将所有代码组织成一行也没有任何意义,很难阅读。 (3认同)
  • 在循环内使用排序不是一个好习惯。只需在比较之前对数组进行一次排序,而不是在每次迭代时都进行排序。 (3认同)
  • 这是一个很好的答案,虽然它有一个警告.回答者甚至强调了警告:**这仅适用于标量数组**但我认为很多人可能会因为使用"标量"这个词而忽略了这个细节,我认为大多数人都不经常听到.通过触及那一块并进一步澄清这个例子,可能会对这个问题做出正义的判断...... (2认同)
  • 或者如果顺序不重要,`array1.length === array2.length &amp;&amp; array1.every(value=&gt; array2.includes(value))` (2认同)

小智 195

我喜欢将Underscore库用于数组/对象重编码项目......在Underscore和Lodash中,无论您是比较数组还是对象,它只是如下所示:

_.isEqual(array1, array2)   // returns a boolean
_.isEqual(object1, object2) // returns a boolean
Run Code Online (Sandbox Code Playgroud)

  • 请注意,顺序很重要`_.isEqual([1,2,3],[2,1,3])=> false` (20认同)
  • 你可以使用_.difference(); 如果订单对你无关紧要 (5认同)
  • 我们可以在此检查之前对数组进行排序,如果顺序无关紧要`_.isEqual([1,2,3] .sort(),[2,1,3] .sort())=> true` (5认同)
  • 或者如果你只想要`isEqual`功能,你总是可以使用lodash.isequal模块 (3认同)

rad*_*tek 101

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

JSON.stringify(a1) === JSON.stringify(a2);
Run Code Online (Sandbox Code Playgroud)

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

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

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

Tim*_*own 61

目前还不清楚"相同"是什么意思.例如,数组ab下面是相同的(注意嵌套数组)?

var a = ["foo", ["bar"]], b = ["foo", ["bar"]];
Run Code Online (Sandbox Code Playgroud)

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

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;
};
Run Code Online (Sandbox Code Playgroud)

  • @GopinathShiva:嗯,只有当你期望它返回'true'时它才会失败.答案解释说它不会.如果需要比较嵌套数组,可以轻松添加递归检查. (4认同)

Tha*_*you 55

实用的方法

我认为,如果一个特定的实现是"正确的方式",如果它只是"正确"("正确")而不是"错误的"解决方案,那是错误的.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)
Run Code Online (Sandbox Code Playgroud)

在我看来,最好的代码甚至不需要评论,这也不例外.这里发生的事情很少,你几乎可以毫不费力地理解这个过程的行为.当然,一些ES6语法现在看起来很陌生,但这只是因为ES6相对较新.

如类型所示,arrayCompare需要比较函数f,和两个输入数组,xs以及ys.在大多数情况下,我们所做的只是调用f (x) (y)输入数组中的每个元素.false如果用户定义的f返回,我们会提前返回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
Run Code Online (Sandbox Code Playgroud)

就那么简单.arrayEqual可以与被定义arrayCompare,并且进行比较的比较功能a,以b使用===(对于全等).

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


宽松的比较

我们可以arrayLooseEqual使用==替代方法轻松定义.现在比较1(Number)和'1'(String)时,结果将是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
Run Code Online (Sandbox Code Playgroud)

深度比较(递归)

你可能已经注意到这只是比较浅的了.当然Tomáš的解决方案是"正确的方式",因为它隐含着深刻的比较,对吗?

好吧,我们的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
Run Code Online (Sandbox Code Playgroud)

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

因为arrayDeepCompare咖喱,我们可以像在前面的例子中那样部分地应用它

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

// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
  arrayDeepCompare (looseEqual)
Run Code Online (Sandbox Code Playgroud)

对我而言,这已经明显改善了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
Run Code Online (Sandbox Code Playgroud)

就那么简单.这里我使用了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
Run Code Online (Sandbox Code Playgroud)

任意比较(例子)

或者,如果你想做一些其他类型的完全随意的比较呢?也许我想知道每个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
Run Code Online (Sandbox Code Playgroud)

少即是多

你可以看到我们用更少的代码实际做得更多.关于arrayCompare它本身并没有什么复杂的,我们制作的每个自定义比较器都有一个非常简单的实现.

轻松地,我们可以准确地定义我们希望如何比较两个数组 - 浅,深,严格,松散,某些对象属性,或某些任意计算,或这些的任意组合 - 所有这些都使用一个过程,arrayCompare.也许甚至梦想着RegExp比较!我知道孩子们喜欢那些正面的...

它是最快的吗?不.但它可能也不需要.如果速度是用于衡量代码质量的唯一指标,那么很多非常好的代码都会被抛弃 - 这就是我称之为"实用方法"的原因.或者,也许更公平,一个实用方法.这个描述适合这个答案,因为我不是说这个答案只是与其他答案相比是实用的; 这是客观真实的.我们已经获得了很高的实用性,只需很少的代码就很容易推理.没有其他代码可以说我们没有获得此描述.

这是否使它成为您的"正确"解决方案?这取决于决定.没有人能为你做到这一点; 只有你知道你的需求是什么.几乎在所有情况下,我都认为简单,实用,通用的代码比聪明和快速的类型.你重视的可能会有所不同,所以选择适合你的方法.


编辑

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

  • "最好的代码甚至不需要评论"......讨厌说出来,但是这段代码可能会使用更多的评论和/或不同的名称 - "比较"非常含糊.如果我正确阅读,你的"比较"本质上是一个曲线递归的"每一个".我认为.或者它是一个咖喱递归的"一些"?嗯.这需要更多的思考而不是必要的.也许更好的名称是"arraysEquivalent",利用"等价关系"的标准术语.或者,甚至更清楚(对我来说),"递归等效". (5认同)
  • 我知道这是一个学习的地方,但我在这里做出一个假设,学习函数式风格的普通程序员可以将任何柯里化函数转换为非柯里化函数。我的回答并没有建议这个 *style* 应该在你自己的程序中使用 – 不柯里化地编写它,使用你自己的缩进规则编写它,按照你想要的方式编写它 – 我以我相信的风格编写我的答案最好地表达了程序。我还想邀请其他人挑战我们在语法上表达程序的方式 (2认同)

uni*_*rio 49

本着原始问题的精神:

我想比较两个阵列......理想情况下,有效率.没有什么花哨的,只要它们是相同的就是真的,如果没有则是假的.

我一直在对这里提出的一些更简单的建议进行性能测试,结果如下(快到慢):

虽然(67%)由蒂姆唐

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

用户 2782196每次(69%)

a1.every((v,i)=> v === a2[i]);
Run Code Online (Sandbox Code Playgroud)

通过DEI减少(74%)

a1.reduce((a, b) => a && a2.includes(b), true);
Run Code Online (Sandbox Code Playgroud)

加入&toString(78%)由Gaizka Allende和vivek

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

a1.toString() === a2.toString();
Run Code Online (Sandbox Code Playgroud)

Victor Palomo的一半(90%)

a1 == a2.toString();
Run Code Online (Sandbox Code Playgroud)

stringify(100%)来自radtek

JSON.stringify(a1) === JSON.stringify(a2);
Run Code Online (Sandbox Code Playgroud)

请注意,下面的示例假设数组是排序的,一维数组..length已经删除了一个共同基准的比较(添加a1.length === a2.length到任何建议,你将获得约10%的性能提升).了解最适合您的解决方案,了解每种解决方案的速度和限制.

无关紧要的说明:有趣的是,人们可以通过向下投票按钮获得所有触发快乐的John Waynes对这个问题的完美合理答案.

  • 这些百分比数字到底意味着什么? (16认同)
  • `join('')` 是危险的,因为 `['foo', 'bar'].join('') == ['foobar'].join('')`。我更喜欢`a1 ==''+ a2`。 (5认同)
  • @TheHumanCat 我很确定最慢的实现代表 100%,而其他实现则代表该时间的一小部分。因此,使用 while 所需的时间是使用 stringify 所需时间的 67%。这有点类似于面包师的百分比...... (3认同)

Eva*_*ner 28

建立TomášZato的答案,我同意只是迭代数组是最快的.另外(就像其他人已经说过的那样),该函数应该被称为equals/equal,而不是比较.鉴于此,我修改了函数以处理比较数组的相似性 - 即它们具有相同的元素,但是无序 - 供个人使用,并且我认为我会把它扔在这里供所有人看.

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;
}
Run Code Online (Sandbox Code Playgroud)

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

例:

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
Run Code Online (Sandbox Code Playgroud)

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


Jef*_*des 12

虽然这有很多答案,但我相信它会有所帮助:

const newArray = [ ...new Set( [...arr1, ...arr2] ) ]
Run Code Online (Sandbox Code Playgroud)

在问题中没有说明数组的结构如何,所以如果你确定你不会在你的数组中有嵌套数组或对象(它发生在我身上,这就是为什么我来到这里回答)上面的代码会起作用.

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

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


EDIT1.

回答德米特里·格林科的问题:"你为什么在这里使用传播操作员(...) - ...新设置?它不起作用"

考虑以下代码:

const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ new Set( [...arr1, ...arr2] ) ]
console.log(newArray)
Run Code Online (Sandbox Code Playgroud)

你会得到

[ Set { 'a', 'b', 'c' } ]
Run Code Online (Sandbox Code Playgroud)

为了使用该值,您需要使用一些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)
Run Code Online (Sandbox Code Playgroud)

你会得到

[ 'a', 'b', 'c' ]
Run Code Online (Sandbox Code Playgroud)

这就是区别,前者会给我一个Set,它也会起作用,因为我可以得到那个Set的大小,但后者给了我需要的数组,更直接的解决方案.


yes*_*nik 10

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

function are_arrs_match(arr1, arr2){
    return arr1.sort().toString() === arr2.sort().toString()
}
Run Code Online (Sandbox Code Playgroud)

让我们来测试一下!

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
Run Code Online (Sandbox Code Playgroud)

  • 我同意上面的评论,但是这个解决方案也适用于我的简单整数数组,其中顺序并不重要,所以我将使用它。 (2认同)

Kam*_*ski 10

最短的

对于数字数组,请尝试:

a1==''+a2
Run Code Online (Sandbox Code Playgroud)

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


Adi*_*tya 9

干得好,

const a = [1, 2, 3]
const b = [1, 2, 3, 4, 5]

const diff = b.filter(e => !a.includes(e))
console.log(diff)
Run Code Online (Sandbox Code Playgroud)

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

const a = [3, 2, 1]
const b = [1, 2, 3, 4, 5]

const diff = b.filter(e => !a.includes(e))
console.log(diff)
Run Code Online (Sandbox Code Playgroud)

如果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)
Run Code Online (Sandbox Code Playgroud)


mic*_*234 9

这里有很多复杂的长答案,所以我只想贡献一个非常简单的答案:使用 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"
Run Code Online (Sandbox Code Playgroud)

  • 因为通常元素的顺序很重要,并且具有不同元素顺序的两个不同数组并不相同。您不能以相同的方式使用它们或使用相同的索引等访问它们。 (2认同)

Ank*_*mar 8

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

if ( arr1.length === arr2.length && arr1.every((a1) => arr2.includes(a1)) ) {
   // logic
}
Run Code Online (Sandbox Code Playgroud)

every()仅当所有元素都通过给定的比较逻辑时才会返回 true。如果在任何迭代中遇到 false,它都会终止并返回 false。

时间复杂度为 O(n*m)。


epa*_*llo 7

与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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

如果订单应保持相同,而不仅仅是一个循环,则不需要排序.

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
Run Code Online (Sandbox Code Playgroud)

  • 第一个版本FAILS:`checkArrays([1,2,3],["1,2",3])== true`,这不太可能是你想要发生的! (6认同)
  • 这只对某些数组有效,而对于大数组则会非常慢. (3认同)
  • 生成JSON也是循环的,你只是(或者看起来如此)不知道它.除了循环之外,生成JSON还需要更多内存 - 它在比较之前创建所述数组的2个字符串表示.实施downwote功能是为了从最好到最差的顺序排序.我认为你的答案不是一个好的答案,所以我对它进行了低估. (2认同)
  • 对不起,我只是说JSON而不是`.join()`.也许如果你说你的第二个解决方案是主要的(因为它是更好的解决方案,虽然对多维数组没有牙齿),我不会那样判断你.到目前为止,我简化了所有将数组转换为字符串的答案.同样,我赞成所有使用正确方法,以防你需要知道.这意味着@Tim Down的答案和Bireys的答案. (2认同)
  • @epascarello:是的,你可以但是(除了你建议的非常长的分隔符的低效率)这意味着会有边缘情况(数组碰巧包含一个带有分隔符的字符串),其中checkArrays()函数行为不当.如果你对数组的内容有所了解,这可能不是问题(所以你可以选择一个你确定不会在数组项中的分隔符),但是如果你正在尝试编写一个*general*数组 - 比较函数,然后使用`join()`就像这样使它巧妙地错! (2认同)
  • ...你*可以使用某种字符串转义机制来修复它,例如将"\"替换为"\\",然后将","替换为"\#"(例如),在`join()之前`ing(确保要连接的各个字符串永远不会包含逗号).然而,考虑到这种方法的努力和低效率,你只需循环遍历数组元素就会好得多! (2认同)

Esq*_*uth 7

这是打字稿版本:

///sf/answers/1150588281/
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
}

///sf/answers/1150588281/
export function arraysDeepEqual<T>(a: Array<T>, b: Array<T>): boolean {
    return JSON.stringify(a) === JSON.stringify(b)
}
Run Code Online (Sandbox Code Playgroud)

摩卡咖啡的一些测试案例:

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)
})
Run Code Online (Sandbox Code Playgroud)


dur*_*tra 7

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

用你的例子看看我的代码,它比较了两个元素是数字的数组,你可以修改或扩展它以用于其他元素类型(通过使用 .join() 而不是 .toString())。

var a1 = [1,2,3];
var a2 = [1,2,3];
const arraysAreEqual = a1.sort().toString()==a2.sort().toString();
// true if both arrays have same elements else false
console.log(arraysAreEqual);
Run Code Online (Sandbox Code Playgroud)


Ham*_*mem 7

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

import {isEqual} from "lodash";

const isTwoArraysEqual = isEqual(array1, array2);
Run Code Online (Sandbox Code Playgroud)

  • 使用 lodash 比较数组既简单又好用。 (2认同)

swi*_*ynx 7

代码高尔夫

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

下面是比较两个 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)

// false positives (see flaws)
console.log("5. ", 1 + ["3"] === 1 + [3]) // type differences
console.log("6. ", 1 + ["1,2"] === 1 + ["1", "2"])
Run Code Online (Sandbox Code Playgroud)

解释

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

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

缺陷

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

编辑:正如评论中所指出的,比较字符串数组会产生误报,例如["1,2"]“等于”到["1", "2"]. 如果您确定这些从未发生过(例如在许多代码高尔夫挑战中),则无需担心。

免责声明

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


Gai*_*nde 6

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

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
Run Code Online (Sandbox Code Playgroud)


Cer*_*nce 6

有一个在 2020 年推出的Stage 1 提案,允许通过添加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
Run Code Online (Sandbox Code Playgroud)

到目前为止,这只是一个暂定提案 - TC39现在将“花时间检查问题空间、解决方案和跨领域关注点”。如果它进入第 2 阶段,它很有可能最终被整合到正确的语言中。


met*_*mit 5

如果您使用的是像一个测试框架摩卡断言库,你可以使用平等比较数组.

expect(a1).to.deep.equal(a2)
Run Code Online (Sandbox Code Playgroud)

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


小智 5

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

arr1.length == arr2.length && arr1.reduce((a, b) => a && arr2.includes(b), true)
Run Code Online (Sandbox Code Playgroud)

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

arr1.length == arr2.length && arr1.reduce((a, b, i) => a && arr2[i], true)
Run Code Online (Sandbox Code Playgroud)
  • length检查确保一个数组中的元素集不仅仅是另一个数组的子集。

  • 减速器用于遍历一个数组并搜索另一数组中的每一项。如果未找到一项,reduce 函数将返回false

    1. 在第一个示例中,正在测试是否包含元素
    2. 第二个示例也检查订单


Nat*_*llo 5

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

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