var a = [1, 2, 3];
var b = [3, 2, 1];
var c = new Array(1, 2, 3);

alert(a == b + "|" + b == c);

demo

如何检查这些数组是否相等,并获得一个方法,如果它们相等,则返回true ?

jQuery是否为此提供了任何方法?


当前回答

根据Tim James的回答和Fox32的评论,下面应该检查空值,假设两个空值不相等。

function arrays_equal(a,b) { return !!a && !!b && !(a<b || b<a); }

> arrays_equal([1,2,3], [1,3,4])
false
> arrays_equal([1,2,3], [1,2,3])
true
> arrays_equal([1,3,4], [1,2,3])
false
> arrays_equal(null, [1,2,3])
false
> arrays_equal(null, null)
false

其他回答

var a= [1, 2, 3, '3'];
var b = [1, 2, 3];

var c = a.filter(function (i) { return ! ~b.indexOf(i); });

alert(c.length);

要做到这一点并不容易。我也需要这个,但我想要一个函数,可以接受任何两个变量,并测试是否相等。这包括非对象值、对象、数组和任何级别的嵌套。

在您的问题中,您提到希望忽略数组中值的顺序。我的解决方案本身并没有这样做,但您可以通过在比较是否相等之前对数组进行排序来实现

我还想将非对象转换为字符串,这样[1,2]===["1",2]

由于我的项目使用了UnderscoreJs,所以我决定将其作为一个mixin,而不是一个独立的函数。

您可以在http://jsfiddle.net/nemesarial/T44W4/上进行测试

这是我的mxin:

_.mixin({
  /**
  Tests for the equality of two variables
    valA: first variable
    valB: second variable
    stringifyStatics: cast non-objects to string so that "1"===1
  **/
  equal:function(valA,valB,stringifyStatics){
    stringifyStatics=!!stringifyStatics;

    //check for same type
    if(typeof(valA)!==typeof(valB)){
      if((_.isObject(valA) || _.isObject(valB))){
        return false;
      }
    }

    //test non-objects for equality
    if(!_.isObject(valA)){
      if(stringifyStatics){
        var valAs=''+valA;
        var valBs=''+valB;
        ret=(''+valA)===(''+valB);
      }else{
        ret=valA===valB;
      }
      return ret;
    }

    //test for length
    if(_.size(valA)!=_.size(valB)){
      return false;
    }

    //test for arrays first
    var isArr=_.isArray(valA);

    //test whether both are array or both object
    if(isArr!==_.isArray(valB)){
      return false;
    }

    var ret=true;
    if(isArr){
      //do test for arrays
      _.each(valA,function(val,idx,lst){
        if(!ret){return;}
        ret=ret && _.equal(val,valB[idx],stringifyStatics);
      });
    }else{
      //do test for objects
      _.each(valA,function(val,idx,lst){
        if(!ret){return;}

        //test for object member exists
        if(!_.has(valB,idx)){
          ret=false;
          return;
        }

        // test for member equality
        ret=ret && _.equal(val,valB[idx],stringifyStatics);
      });

    }
    return ret;
  }
});

下面是它的用法:

_.equal([1,2,3],[1,2,"3"],true)

要演示嵌套,你可以这样做:

_.equal(
    ['a',{b:'b',c:[{'someId':1},2]},[1,2,3]],
    ['a',{b:'b',c:[{'someId':"1"},2]},["1",'2',3]]
,true);

[2021更新日志:option4的错误修复:对js对象没有总排序(甚至不包括NaN!]=NaN和'5'==5('5'== 5,'2'<3,等等)),所以不能在Map.keys()上使用.sort(cmpFunc)(尽管你可以在Object.keys(obj)上使用,因为即使'数值'键也是字符串)。]

选项1

最简单的选项,工作在几乎所有情况下,除了null!==undefined但它们都被转换为JSON表示null并被认为相等:

function arraysEqual(a1,a2) {
    /* WARNING: arrays must not contain {objects} or behavior may be undefined */
    return JSON.stringify(a1)==JSON.stringify(a2);
}

(This might not work if your array contains objects. Whether this still works with objects depends on whether the JSON implementation sorts keys. For example, the JSON of {1:2,3:4} may or may not be equal to {3:4,1:2}; this depends on the implementation, and the spec makes no guarantee whatsoever. [2017 update: Actually the ES6 specification now guarantees object keys will be iterated in order of 1) integer properties, 2) properties in the order they were defined, then 3) symbol properties in the order they were defined. Thus IF the JSON.stringify implementation follows this, equal objects (in the === sense but NOT NECESSARILY in the == sense) will stringify to equal values. More research needed. So I guess you could make an evil clone of an object with properties in the reverse order, but I cannot imagine it ever happening by accident...] At least on Chrome, the JSON.stringify function tends to return keys in the order they were defined (at least that I've noticed), but this behavior is very much subject to change at any point and should not be relied upon. If you choose not to use objects in your lists, this should work fine. If you do have objects in your list that all have a unique id, you can do a1.map(function(x)}{return {id:x.uniqueId}}). If you have arbitrary objects in your list, you can read on for option #2.)

这也适用于嵌套数组。

但是,由于创建这些字符串和对它们进行垃圾回收的开销,它的效率略低。


选项2

历史版本1解决方案:

// generally useful functions function type(x) { // does not work in general, but works on JSONable objects we care about... modify as you see fit // e.g. type(/asdf/g) --> "[object RegExp]" return Object.prototype.toString.call(x); } function zip(arrays) { // e.g. zip([[1,2,3],[4,5,6]]) --> [[1,4],[2,5],[3,6]] return arrays[0].map(function(_,i){ return arrays.map(function(array){return array[i]}) }); } // helper functions function allCompareEqual(array) { // e.g. allCompareEqual([2,2,2,2]) --> true // does not work with nested arrays or objects return array.every(function(x){return x==array[0]}); } function isArray(x){ return type(x)==type([]) } function getLength(x){ return x.length } function allTrue(array){ return array.reduce(function(a,b){return a&&b},true) } // e.g. allTrue([true,true,true,true]) --> true // or just array.every(function(x){return x}); function allDeepEqual(things) { // works with nested arrays if( things.every(isArray) ) return allCompareEqual(things.map(getLength)) // all arrays of same length && allTrue(zip(things).map(allDeepEqual)); // elements recursively equal //else if( this.every(isObject) ) // return {all have exactly same keys, and for // each key k, allDeepEqual([o1[k],o2[k],...])} // e.g. ... && allTrue(objectZip(objects).map(allDeepEqual)) //else if( ... ) // extend some more else return allCompareEqual(things); } // Demo: allDeepEqual([ [], [], [] ]) true allDeepEqual([ [1], [1], [1] ]) true allDeepEqual([ [1,2], [1,2] ]) true allDeepEqual([ [[1,2],[3]], [[1,2],[3]] ]) true allDeepEqual([ [1,2,3], [1,2,3,4] ]) false allDeepEqual([ [[1,2],[3]], [[1,2],[],3] ]) false allDeepEqual([ [[1,2],[3]], [[1],[2,3]] ]) false allDeepEqual([ [[1,2],3], [1,[2,3]] ]) false <!-- More "proper" option, which you can override to deal with special cases (like regular objects and null/undefined and custom objects, if you so desire): To use this like a regular function, do: function allDeepEqual2() { return allDeepEqual([].slice.call(arguments)); } Demo: allDeepEqual2([[1,2],3], [[1,2],3]) true -->


选项3

function arraysEqual(a,b) { /* Array-aware equality checker: Returns whether arguments a and b are == to each other; however if they are equal-lengthed arrays, returns whether their elements are pairwise == to each other recursively under this definition. */ if (a instanceof Array && b instanceof Array) { if (a.length!=b.length) // assert same length return false; for(var i=0; i<a.length; i++) // assert each element equal if (!arraysEqual(a[i],b[i])) return false; return true; } else { return a==b; // if not both arrays, should be the same } } //Examples: arraysEqual([[1,2],3], [[1,2],3]) true arraysEqual([1,2,3], [1,2,3,4]) false arraysEqual([[1,2],[3]], [[1,2],[],3]) false arraysEqual([[1,2],[3]], [[1],[2,3]]) false arraysEqual([[1,2],3], undefined) false arraysEqual(undefined, undefined) true arraysEqual(1, 2) false arraysEqual(null, null) true arraysEqual(1, 1) true arraysEqual([], 1) false arraysEqual([], undefined) false arraysEqual([], []) true /* If you wanted to apply this to JSON-like data structures with js Objects, you could do so. Fortunately we're guaranteed that all objects keys are unique, so iterate over the objects OwnProperties and sort them by key, then assert that both the sorted key-array is equal and the value-array are equal, and just recurse. We CANNOT extend the sort-then-compare method with Maps as well; even though Map keys are unique, there is no total ordering in ecmascript, so you can't sort them... but you CAN query them individually (see the next section Option 4). (Also if we extend this to Sets, we run into the tree isomorphism problem http://logic.pdmi.ras.ru/~smal/files/smal_jass08_slides.pdf - fortunately it's not as hard as general graph isomorphism; there is in fact an O(#vertices) algorithm to solve it, but it can get very complicated to do it efficiently. The pathological case is if you have a set made up of lots of seemingly-indistinguishable objects, but upon further inspection some of those objects may differ as you delve deeper into them. You can also work around this by using hashing to reject almost all cases.) */ <!-- **edit**: It's 2016 and my previous overcomplicated answer was bugging me. This recursive, imperative "recursive programming 101" implementation keeps the code really simple, and furthermore fails at the earliest possible point (giving us efficiency). It also doesn't generate superfluous ephemeral datastructures (not that there's anything wrong with functional programming in general, but just keeping it clean here). If we wanted to apply this to a non-empty arrays of arrays, we could do seriesOfArrays.reduce(arraysEqual). This is its own function, as opposed to using Object.defineProperties to attach to Array.prototype, since that would fail with a key error if we passed in an undefined value (that is however a fine design decision if you want to do so). This only answers OPs original question. -->


选项4: (续2016年编辑)

这应该适用于大多数对象:

const STRICT_EQUALITY_BROKEN = (a,b)=> a===b; const STRICT_EQUALITY_NO_NAN = (a,b)=> { if (typeof a=='number' && typeof b=='number' && ''+a=='NaN' && ''+b=='NaN') // isNaN does not do what you think; see +/-Infinity return true; else return a===b; }; function deepEquals(a,b, areEqual=STRICT_EQUALITY_NO_NAN, setElementsAreEqual=STRICT_EQUALITY_NO_NAN) { /* compares objects hierarchically using the provided notion of equality (defaulting to ===); supports Arrays, Objects, Maps, ArrayBuffers */ if (a instanceof Array && b instanceof Array) return arraysEqual(a,b, areEqual); if (Object.getPrototypeOf(a)===Object.prototype && Object.getPrototypeOf(b)===Object.prototype) return objectsEqual(a,b, areEqual); if (a instanceof Map && b instanceof Map) return mapsEqual(a,b, areEqual); if (a instanceof Set && b instanceof Set) { if (setElementsAreEqual===STRICT_EQUALITY_NO_NAN) return setsEqual(a,b); else throw "Error: set equality by hashing not implemented because cannot guarantee custom notion of equality is transitive without programmer intervention." } if ((a instanceof ArrayBuffer || ArrayBuffer.isView(a)) && (b instanceof ArrayBuffer || ArrayBuffer.isView(b))) return typedArraysEqual(a,b); return areEqual(a,b); // see note[1] -- IMPORTANT } function arraysEqual(a,b, areEqual) { if (a.length!=b.length) return false; for(var i=0; i<a.length; i++) if (!deepEquals(a[i],b[i], areEqual)) return false; return true; } function objectsEqual(a,b, areEqual) { var aKeys = Object.getOwnPropertyNames(a); var bKeys = Object.getOwnPropertyNames(b); if (aKeys.length!=bKeys.length) return false; aKeys.sort(); bKeys.sort(); for(var i=0; i<aKeys.length; i++) if (!areEqual(aKeys[i],bKeys[i])) // keys must be strings return false; return deepEquals(aKeys.map(k=>a[k]), aKeys.map(k=>b[k]), areEqual); } function mapsEqual(a,b, areEqual) { // assumes Map's keys use the '===' notion of equality, which is also the assumption of .has and .get methods in the spec; however, Map's values use our notion of the areEqual parameter if (a.size!=b.size) return false; return [...a.keys()].every(k=> b.has(k) && deepEquals(a.get(k), b.get(k), areEqual) ); } function setsEqual(a,b) { // see discussion in below rest of StackOverflow answer return a.size==b.size && [...a.keys()].every(k=> b.has(k) ); } function typedArraysEqual(a,b) { // we use the obvious notion of equality for binary data a = new Uint8Array(a); b = new Uint8Array(b); if (a.length != b.length) return false; for(var i=0; i<a.length; i++) if (a[i]!=b[i]) return false; return true; } Demo (not extensively tested): var nineTen = new Float32Array(2); nineTen[0]=9; nineTen[1]=10; > deepEquals( [[1,[2,3]], 4, {a:5,'111':6}, new Map([['c',7],['d',8]]), nineTen], [[1,[2,3]], 4, {111:6,a:5}, new Map([['d',8],['c',7]]), nineTen] ) true > deepEquals( [[1,[2,3]], 4, {a:'5','111':6}, new Map([['c',7],['d',8]]), nineTen], [[1,[2,3]], 4, {111:6,a:5}, new Map([['d',8],['c',7]]), nineTen], (a,b)=>a==b ) true

Note that if one is using the == notion of equality, then know that falsey values and coercion means that == equality is NOT TRANSITIVE. For example ''==0 and 0=='0' but ''!='0'. This is relevant for Sets: I do not think one can override the notion of Set equality in a meaningful way. If one is using the built-in notion of Set equality (that is, ===), then the above should work. However if one uses a non-transitive notion of equality like ==, you open a can of worms: Even if you forced the user to define a hash function on the domain (hash(a)!=hash(b) implies a!=b) I'm not sure that would help... Certainly one could do the O(N^2) performance thing and remove pairs of == items one by one like a bubble sort, and then do a second O(N^2) pass to confirm things in equivalence classes are actually == to each other, and also != to everything not thus paired, but you'd STILL have to throw a runtime error if you have some coercion going on... You'd also maybe get weird (but potentially not that weird) edge cases with https://developer.mozilla.org/en-US/docs/Glossary/Falsy and Truthy values (with the exception that NaN==NaN... but just for Sets!). This is not an issue usually with most Sets of homogenous datatype.

总结集合上递归等式的复杂性:

Set equality is the tree isomorphism problem http://logic.pdmi.ras.ru/~smal/files/smal_jass08_slides.pdf but a bit simpler set A =? set B being synonymous with B.has(k) for every k in A implicitly uses ===-equality ([1,2,3]!==[1,2,3]), not recursive equality (deepEquals([1,2,3],[1,2,3]) == true), so two new Set([[1,2,3]]) would not be equal because we don't recurse trying to get recursive equality to work is kind of meaningless if the recursive notion of equality you use is not 1) reflexive (a=b implies b=a) and 2) symmetric (a=a) and 3) transitive (a=b and b=c implies a=c); this is the definition of an equivalence class the equality == operator obviously does not obey many of these properties even the strict equality === operator in ecmascript does not obey these properties, because the strict equality comparison algorithm of ecmascript has NaN!=NaN; this is why many native datatypes like Set and Map 'equate' NaNs to consider them the same values when they appear as keys As long as we force and ensure recursive set equality is indeed transitive and reflexive and symmetric, we can make sure nothing horribly wrong happens. Then, we can do O(N^2) comparisons by recursively comparing everything randomly, which is incredibly inefficient. There is no magical algorithm that lets us do setKeys.sort((a,b)=> /*some comparison function*/) because there is no total ordering in ecmascript (''==0 and 0=='0', but ''!='0'... though I believe you might be able to define one yourself which would certainly be a lofty goal). We can however .toStringify or JSON.stringify all elements to assist us. We will then sort them, which gives us equivalence classes (two same things won't not have the same string JSON representation) of potentially-false-positives (two different things may have the same string or JSON representation). However, this introduces its own performance issues because serializing the same thing, then serializing subsets of that thing, over and over, is incredibly inefficient. Imagine a tree of nested Sets; every node would belong to O(depth) different serializations! Even if that was not an issue, the worst-case performance would still be O(N!) if all the serializations 'hints' were the same

因此,上面的实现声明如果项只是简单的===(不是递归地===),则set是相等的。这意味着对于new Set([1,2,3])和new Set([1,2,3]),它将返回false。如果您知道自己在做什么,那么只需花点功夫,就可以重写这部分代码。

(sidenote: Maps are es6 dictionaries. I can't tell if they have O(1) or O(log(N)) lookup performance, but in any case they are 'ordered' in the sense that they keep track of the order in which key-value pairs were inserted into them. However, the semantic of whether two Maps should be equal if elements were inserted in a different order into them is ambiguous. I give a sample implementation below of a deepEquals that considers two maps equal even if elements were inserted into them in a different order.)

(note [1]: IMPORTANT: NOTION OF EQUALITY: You may want to override the noted line with a custom notion of equality, which you'll also have to change in the other functions anywhere it appears. For example, do you or don't you want NaN==NaN? By default this is not the case. There are even more weird things like 0=='0'. Do you consider two objects to be the same if and only if they are the same object in memory? See https://stackoverflow.com/a/5447170/711085 . You should document the notion of equality you use.) Also note that other answers which naively use .toString and .sort may sometimes fall pray to the fact that 0!=-0 but are considered equal and canonicalizable to 0 for almost all datatypes and JSON serialization; whether -0==0 should also be documented in your notion of equality, as well as most other things in that table like NaN, etc.

您应该能够将上述扩展到弱映射、弱集。不确定扩展到DataViews是否有意义。应该也能够扩展到regexp等等。

当您扩展它时,您会意识到您做了许多不必要的比较。这就是我之前定义的类型函数(解决方案#2)可以派上用场的地方;然后你就可以立即调度。这是否值得(可能吗?)不确定它在引擎盖下是如何工作的)字符串表示类型是由你。你可以重写dispatcher,也就是deepEquals函数,像这样:

var dispatchTypeEquals = {
    number: function(a,b) {...a==b...},
    array: function(a,b) {...deepEquals(x,y)...},
    ...
}
function deepEquals(a,b) {
    var typeA = extractType(a);
    var typeB = extractType(a);
    return typeA==typeB && dispatchTypeEquals[typeA](a,b);
}

对于数字和字符串等原始值,这是一个简单的解决方案:

a = [1,2,3]

b = [3,2,1]

a.sort().toString() == b.sort().toString() 

调用sort()将确保元素的顺序无关紧要。toString()调用将创建一个值以逗号分隔的字符串,以便可以测试两个字符串是否相等。

这是你应该做的。请不要使用stringify或< >。

function arraysEqual(a, b) {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (a.length !== b.length) return false;

  // If you don't care about the order of the elements inside
  // the array, you should sort both arrays here.
  // Please note that calling sort on an array will modify that array.
  // you might want to clone your array first.

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