Параўнанне масіваў як (мульты-) мноства

Я шукаю эфектыўны спосаб, каб высветліць, ці ўтрымліваюць два масіва аднолькавыя колькасці аднолькавых элементаў (у == сэнсе), у любым парадку:

foo = {/*some object*/}
bar = {/*some other object*/}

a = [1,2,foo,2,bar,2]
b = [bar,2,2,2,foo,1]

sameElements(a, b) --> true

PS. Звярніце ўвагу, што ў значнай ступені кожнае рашэнне ў струмень выкарыстоўвае === , а не == для параўнання. Гэта добра для маіх патрэбаў, хоць.

9
дададзена аўтар BeNdErR, крыніца
@ C5H8NNaO4: Вялікі дзякуй яшчэ раз за вашу спагадлівасць і энтузіязм!
дададзена аўтар georg, крыніца
Ну, як правіла, Foo і бар ніколі не будзе роўных у == пачуццёвых, ці вы хочаце, каб параўнаць яны ўласцівасці (глыбока?) У у выпадку элемента аб'екта?
дададзена аўтар Matt, крыніца
@FelixKling: Добры пытанне.
дададзена аўтар Matt, крыніца
Выкарыстоўвайце Set або <�код > Карта (з колькасцю асобнікаў) замест масіва :-)
дададзена аўтар Bergi, крыніца
Што менавіта вы хочаце, каб прысудзіць узнагароду за? Акрамя таго, просьба апісаць сярэднія кампаненты MultiSet: Колькі прымітывы, колькі аб'ектаў знаходзяцца там? Як часта яны адбываюцца некалькі разоў (напрыклад, аб'екты ў вашым прыкладзе не робяць наогул)? Гэта дапаможа атрымаць больш тэстаў прадстаўніка прадукцыйнасці і больш аптымальныя рашэнні.
дададзена аўтар Bergi, крыніца
@ Matt: Foo і бар не павінны быць роўныя. Масівы ўтрымліваюць як аб'екты, Foo і бар . Я думаю, што гэта кропка (FWIW, я быў зьбянтэжаны першым, а).
дададзена аўтар Felix Kling, крыніца
@ Thg435 Я абнавіў адказ, я здолеў аптымізаваць яго трохі, даючы яму прырост прадукцыйнасці каля 100%
дададзена аўтар C5H8NNaO4, крыніца
Я дадаў новы адказ, працуе ад батарэі на маім Maching на хром пры ~ 100ops/s на светлячок ў ~ 80ops/с.
дададзена аўтар C5H8NNaO4, крыніца
@ Thg435 Вы можаце :) гэта было прыемна, я люблю такія рэчы, як гэта;)
дададзена аўтар C5H8NNaO4, крыніца

8 адказы

абнаўленне 5 I posted a new answer with a different approach.

абнаўленне

I extended the code to have the possibility of either checking by reference or equality

проста перадаць True у якасці другога параметру, каб зрабіць эталонную праверку.

Таксама я дадаў прыклад <�моцны> JSPerf </моцны>

  • Ён працуе каля 11 OPS/с робяць спасылачныя чэк

I will comment the code as soon(!) as I get some spare time to explain it a bit more, but at the moment don't have the time for that, sry. Done

абнаўленне 2.

Like Bruno pointed out in the comments sameElements([NaN],[NaN]) yields false

На мой погляд, гэта правільнае паводзіны як NaN з'яўляецца ambigious і заўсёды павінна прывесці да хлусня вынік, па меншай меры, пры параўнанні NaN.equals (NaN) . Але ў яго быў даволі добры момант.

Ці

[1,2,foo,bar,NaN,3] should be equal to [1,3,foo,NaN,bar,2] or not.

Ok.. honestly I'm a bit torn Ціit should or not, so i дабаўленайtwo flags.

  • Number.prototype.equal.NaN
    • If true
      • NaN.equals(NaN) //true
  • Array.prototype.equal.NaN
    • If true
      • [NaN].equals([NaN],true) //true
      • note this is only for reference checks. As a deep check would invoke Number.prototype.equals anyway

абнаўленне 3:

Dang я цалкам прапусціў 2 радкі ў функцыі сартавання.

дабаўленай

 r[0] = a._srt; //DANG i totally missed this line
 r[1] = b._srt; //And this.

Line 105 in the Fiddle

Які выгляд важны, паколькі ён вызначае паслядоўны парадак элементаў.

абнаўленне 4
I tried to optimize the sort function a bit, and managed to get it up to about 20 ops/s.

Below is the абнаўленнеd code, as well as the абнаўленнеd fiddle =)

Таксама я вырашыў пазначыць аб'екты за межамі функцыі сартавання, яна не здаецца, каб зрабіць розніцу ў прадукцыйнасці больш, і яго больш зручнай для чытання


Вось падыход, выкарыстоўваючы Object.defineProperty дадаць роўны функцыі ў

Array,Object,Number,String,Boolean's prototype to avoid typechecking in one function for performance reasons. As we can recursively call .equals on any element.

Але, вядома, праверка аб'ектаў на роўнасць можа выклікаць праблемы з прадукцыйнасцю ў вялікіх аб'ектаў.

Так што, калі хтосьці адчувае непрыемны маніпулюючы родныя прататыпы, проста зрабіць праверку тыпу і змясціць яго ў адну функцыю

Object.defineProperty(Boolean.prototype, "equals", {
        enumerable: false,
        configurable: true,
        value: function (c) {
            return this == c; //For booleans simply return the equality
        }
    });

Object.defineProperty(Number.prototype, "equals", {
        enumerable: false,
        configurable: true,
        value: function (c) {
            if (Number.prototype.equals.NaN == true && isNaN(this) && c != c) return true; //let NaN equals NaN if flag set
            return this == c;//else do a normal compare
        }
    });

Number.prototype.equals.NaN = false; //Set to true to return true for NaN == NaN

Object.defineProperty(String.prototype, "equals", {
        enumerable: false,
        configurable: true,
        value: Boolean.prototype.equals //the same (now we covered the primitives)
    });

Object.defineProperty(Object.prototype, "equals", {
        enumerable: false,
        configurable: true,
        value: function (c, reference) {
            if (true === reference) //If its a check by reference
                return this === c; //return the result of comparing the reference
            if (typeof this != typeof c) { 
                return false; //if the types don't match (Object equals primitive) immediately return
            }
            var d = [Object.keys(this), Object.keys(c)],//create an array with the keys of the objects, which get compared
                f = d[0].length; //store length of keys of the first obj (we need it later)
            if (f !== d[1].length) {//If the Objects differ in the length of their keys
                return false; //immediately return
            }
            for (var e = 0; e < f; e++) { //iterate over the keys of the first object
                if (d[0][e] != d[1][e] || !this[d[0][e]].equals(c[d[1][e]])) {
                    return false; //if either the key name does not match or the value does not match, return false. a call of .equal on 2 primitives simply compares them as e.g Number.prototype.equal gets called
                }
            }
            return true; //everything is equal, return true
        }
    });
Object.defineProperty(Array.prototype, "equals", {
        enumerable: false,
        configurable: true,
        value: function (c,reference) {

            var d = this.length;
            if (d != c.length) {
                return false;
            }
            var f = Array.prototype.equals.sort(this.concat());
            c = Array.prototype.equals.sort(c.concat(),f)

            if (reference){
                for (var e = 0; e < d; e++) {
                    if (f[e] != c[e] && !(Array.prototype.equals.NaN && f[e] != f[e] && c[e] != c[e])) {
                        return false;
                    }
                }                
            } else {
                for (var e = 0; e < d; e++) {
                    if (!f[e].equals(c[e])) {
                        return false;
                    }
                }
            }
            return true;

        }
    });

Array.prototype.equals.NaN = false; //Set to true to allow [NaN].equals([NaN]) //true

Object.defineProperty(Array.prototype.equals,"sort",{
  enumerable:false,
  value:function sort (curr,prev) {
         var weight = {
            "[object Undefined]":6,         
            "[object Object]":5,
            "[object Null]":4,
            "[object String]":3,
            "[object Number]":2,
            "[object Boolean]":1
        }
        if (prev) { //mark the objects
            for (var i = prev.length,j,t;i>0;i--) {
                t = typeof (j = prev[i]);
                if (j != null && t === "object") {
                     j._pos = i;   
                } else if (t !== "object" && t != "undefined" ) break;
            }
        }

        curr.sort (sorter);

        if (prev) {
            for (var k = prev.length,l,t;k>0;k--) {
                t = typeof (l = prev[k]);
                if (t === "object" && l != null) {
                    delete l._pos;
                } else if (t !== "object" && t != "undefined" ) break;
            }
        }
        return curr;

        function sorter (a,b) {

             var tStr = Object.prototype.toString
             var types = [tStr.call(a),tStr.call(b)]
             var ret = [0,0];
             if (types[0] === types[1] && types[0] === "[object Object]") {
                 if (prev) return a._pos - b._pos
                 else {
                     return a === b ? 0 : 1;
                 }
             } else if (types [0] !== types [1]){
                     return weight[types[0]] - weight[types[1]]
             }



            return a>b?1:a

Пры гэтым мы можам зменшыць sameElements функцыя

function sameElements(c, d,referenceCheck) {
     return c.equals(d,referenceCheck);  //call .equals of Array.prototype.
}

Заўвага. вядома, вы маглі б паставіць усе роўныя функцыі ў sameElements функцыю, для кошту праверкі тыпаў.

Цяпер тут 3 прыкладу: 1 з глыбокай праверкай, 2 з эталоннай праверкай.

var foo = {
    a: 1,
    obj: {
        number: 2,
        bool: true,
        string: "asd"
    },
    arr: [1, 2, 3]
};

var bar = {
    a: 1,
    obj: {
        number: 2,
        bool: true,
        string: "asd"
    },
    arr: [1, 2, 3]
};

var foobar = {
    a: 1,
    obj: {
        number: 2,
        bool: true,
        string: "asd"
    },
    arr: [1, 2, 3, 4]
};

var a = [1, 2, foo, 2, bar, 2];
var b = [foo, 2, 2, 2, bar, 1];
var c = [bar, 2, 2, 2, bar, 1];

Так што гэтыя Масівы мы параўноўваем. І выхад

  1. Check a and b with references only.

    console.log (sameElements ( a,b,true)) //true As they contain the same elements

  2. Check b and c with references only

    console.log (sameElements (b,c,true)) //false as c contains bar twice.

  3. Check b and c deeply

    console.log (sameElements (b,c,false)) //true as bar and foo are equal but not the same

  4. Check for 2 Arrays containing NaN

    Array.prototype.equals.NaN = true;
    console.log(sameElements([NaN],[NaN],true)); //true.
    Array.prototype.equals.NaN = false;

<�Моцны> Дэманстрацыя на JSFiddle

7
дададзена
@ C5H8NNaO4 На жаль, проста хацеў паглядзець, што адбудзецца, калі аб'екты выкарыстоўваюцца мела больш глыбокія структуры. Аказалася, што ваш алгарытм быў яшчэ хутчэй, па меншай меры, на храмаванай :-). Калі вы хочаце, вы можаце вярнуць яго назад.
дададзена аўтар Bruno, крыніца
@ C5H8NNaO4 Nice прадукцыйнасць хоць :-)
дададзена аўтар Bruno, крыніца
Я разумею вашу кропку гледжання, але згодна вам тое, што павінна наступны зваротны званок sameElements ([1,2, Foo, 2, бар, 2, NaN], [бар, 2,2,2, Foo, 1, NaN ], праўда)
дададзена аўтар Bruno, крыніца
@ C5H8NNaO4 sameElements ([NaN], [NaN], праўда) вяртае хлусня
дададзена аўтар Bruno, крыніца
На жаль, я не разумею, што робіць ваш код. Сыход растлумачыць трохі больш?
дададзена аўтар georg, крыніца
добрая праца, дзякуй!
дададзена аўтар georg, крыніца
@Bruno Дарэчы, што было змяненне ў абарот 13 JSPerf?
дададзена аўтар C5H8NNaO4, крыніца
@Bruno Нах няма неабходнасці прасіць прабачэння! гэта ваш парфюм так ці інакш;), таксама гэта цікавы момант, як добра, я зусім нармальна з гэтым =) я быў толькі цікава, што змянілася, так як я не хацеў, каб чытаць гэтыя 500 некалькі радкоў кода. Так, гэта здаецца, але вашы коды ісці далёка наперадзе на светлячок
дададзена аўтар C5H8NNaO4, крыніца
@Bruno Дзякуй =), я абнавіў адказ і скрыпку са сцягамі [NaN] .equals ([NaN]) і NaN.equals (NaN) , каб атрымаць праўдзівы ,
дададзена аўтар C5H8NNaO4, крыніца
<�Я> (выдалены каментар :) </я> я казаў: Вядома, як NaN == NaN//дакладна і павінна быць ... Як Ня лік </код! > Неадназначны. гэта запатрабуе праверкі ў Number.prototype.equal - (! это = гэта && з = с)> калі вярнуцца дакладна @Bruno Ну, вось з іншага боку. То добрая кропка а, (я выдаліў свой каментар.) І я буду абнаўляць адказ з опцыяй у Array.prototype.equal , каб вярнуцца дакладна для NaN =) дзякуй за ўказанне на гэта.
дададзена аўтар C5H8NNaO4, крыніца
@ Thg435 Я спрабаваў растлумачыць гэта крыху лепш, каментуючы код. (Я не так добра ў тлумачэнні, так, калі пакінуць там якія-небудзь пытанні, я паспрабую растлумачыць іх зноў, проста змесціце каментар) =)
дададзена аўтар C5H8NNaO4, крыніца
@ Thg435 Прывітанне =) Я абнавіў адказ, ён цяпер ці спасылка або праверка роўнасці. і дадаць яго да JSPerf. Я растлумачу гэта, як толькі я знаходжу час для гэтага (я проста буду каментаваць яго тут, а не ў скрыпцы)
дададзена аўтар C5H8NNaO4, крыніца
Ох .. здаецца, я прапусціў кропку і толькі кантрольная праверка неабходная, мой дрэнны
дададзена аўтар C5H8NNaO4, крыніца
Які працуе ў 8ops/с на вар а = JSON.parse ( "[" + новы Array (10000) .join (JSON.stringify ({а: 1, бы: 2, C: {а: 3}, д: [1,2,3,4 & ZWNJ;]}) + "") + {}] '); 10k масіў як гэта
дададзена аўтар C5H8NNaO4, крыніца

<�Моцны> UPDATE

Як @Bergi і @ thg435 момант з маёй папярэдняй рэалізацыі быў сапсаваны, так вось яшчэ рэалізацыя:

function sameElements(a, b) {
    var objs = [];
   //if length is not the same then must not be equal
    if (a.length != b.length) return false;

   //do an initial sort which will group types
    a.sort();
    b.sort();

    for ( var i = 0; i < a.length; i++ ) {

        var aIsPrimitive = isPrimitive(a[i]);
        var bIsPrimitive = isPrimitive(b[i]);

       //NaN will not equal itself
        if( a[i] !== a[i] ) {
            if( b[i] === b[i] ) {
                return false;
            }
        }
        else if (aIsPrimitive && bIsPrimitive) {

            if( a[i] != b[i] ) return false;
        }
       //if not primitive increment the __count property
        else if (!aIsPrimitive && !bIsPrimitive) {
            incrementCountA(a[i]);
            incrementCountB(b[i]);
           //keep track on non-primitive objects
            objs.push(i);
        }
       //if both types are not the same then this array
       //contains different number of primitives
        else {
            return false;
        }

    }

    var result = true;

    for (var i = 0; i < objs.length; i++) {
        var ind = objs[i];
       //if __aCount and __bCount match then object exists same
       //number of times in both arrays
        if( a[ind].__aCount !== a[ind].__bCount ) result = false;
        if( b[ind].__aCount !== b[ind].__bCount ) result = false;

       //revert object to what it was 
       //before entering this function
        delete a[ind].__aCount;
        delete a[ind].__bCount;
        delete b[ind].__aCount;
        delete b[ind].__bCount;
    }

    return result;
}

// inspired by @Bergi's code
function isPrimitive(arg) {
    return Object(arg) !== arg;
}

function incrementCountA(arg) {
    if (arg.hasOwnProperty("__aCount")) {
        arg.__aCount = arg.__aCount + 1;
    } else {
        Object.defineProperty(arg, "__aCount", {
            enumerable: false,
            value: 1,
            writable: true,
            configurable: true
        });
    }
}
function incrementCountB(arg) {
    if (arg.hasOwnProperty("__bCount")) {
        arg.__bCount = arg.__bCount + 1;
    } else {
        Object.defineProperty(arg, "__bCount", {
            enumerable: false,
            value: 1,
            writable: true,
            configurable: true
        });
    }
}

Тады проста выклічце функцыю

sameElements( ["NaN"], [NaN] );//false

// As "1" == 1 returns true
sameElements( [1],["1"] );//true

sameElements( [1,2], [1,2,3] ); //false

Вышэй рэалізацыі фактычна вызначае новае ўласцівасць, званае «__count», які выкарыстоўваецца для адсочвання непримитивных элементаў у абодвух масівах. Яны будуць выдаленыя да таго, як функцыя вяртае так, каб пакінуць элементы масіва, як і раней.

Fiddle here

JSPerf .

Таму я змяніў тэст JSPerf справа ў тым, што, як @Bergi сцвярджае тэставыя масівы, асабліва той факт, былі толькі 2 унікальных аб'ектаў ва ўсім масіве не з'яўляецца прадстаўніком таго, што мы тэстуем для.

Яшчэ адным перавагі дадзенай рэалізацыі з'яўляецца тое, што калі вам трэба, каб зрабіць яго сумяшчальным з папярэдне IE9 браўзэрамі замест выкарыстання defineProperty стварыць незлічоная ўласцівасць, якое вы маглі б проста выкарыстоўваць нармальнае ўласцівасць.

2
дададзена
@ Thg435 Проста абнавіў свой адказ з кодам я працаваў на, а таксама тэсты JSPerf :-)
дададзена аўтар Bruno, крыніца
@Bergi Гэта сапраўды ўдалося дабіцца поспеху. Прычына заключаецца ў тым, што, хоць элементы сартуюцца, для любога элемента, які не з'яўляецца прымітыўным, які ў гэтым выпадку Foo і бары не з'яўляецца, функцыя выкарыстоўвае Array.indexOf спосаб, каб праверыць, што элемент існуе ў абодва масіва. Праверце скрыпку .
дададзена аўтар Bruno, крыніца
@Bergi Дзякуй за ўказанне, што з :( я на круты крывой навучання. Выпуск быў выпраўлены ў цяперашні час.
дададзена аўтар Bruno, крыніца
Гэтая версія сапраўды не працуе правільна, але спасылка JSPerf паказвае яшчэ некалькі версій ад вас - разгледзець перепроведение іх тут.
дададзена аўтар georg, крыніца
Гэта не працуе, як добра. Праверыць sameElements ([о = {}, о], [р = {}, р]) , які дае True !
дададзена аўтар Bergi, крыніца
Пры сартаванні гэта добрая ідэя, гэта не будзе працаваць на sameElements ([ бар, Foo], [бар, Foo, Foo])
дададзена аўтар Bergi, крыніца
Але ён павінен вяртаць хлусня ...
дададзена аўтар Bergi, крыніца
Я дадаў свой прыклад на JSPerf , а таксама. <�Я> ён дае False для sameElements ([O = {}, о], [р = {}, P], праўда)
дададзена аўтар C5H8NNaO4, крыніца

Як гэта магчыма?

var foo = {}; var bar=[];
var a = [3,2,1,foo]; var b = [foo,1,2,3];

function comp(a,b)
{
   //immediately discard if they are of different sizes
    if (a.length != b.length) return false;

    b = b.slice(0);//clone to keep original values after the function

    a.forEach(function(e) {
        var i;
        if ((i = b.indexOf(e)) != -1)
            b.splice(i, 1);
    });

    return !b.length;
}

comp(a,b);
2
дададзена
Працуе добра, але ў вас і рашэнні Фрэдэрыка апынуліся даволі павольнымі для маіх патрэбаў.
дададзена аўтар georg, крыніца
здаецца, працуе, але не выглядае асабліва эфектыўным. лустачку, затым склеіць ... Я не ведаю. Мае масівы маюць 10000-х элементаў.
дададзена аўтар georg, крыніца
Зрэз неабходна кланаваць масіў. Калі вы не збіраецеся выкарыстоўваць яго зноў можна выдаліць гэтыя 2 радкі ... На самай справе вам не трэба кланаваць а , я буду абнаўляць, што ...
дададзена аўтар BrunoLM, крыніца

Вы можаце рэалізаваць наступны алгарытм:

  • If a and b do not have the same length:
    • Return false.
  • Otherwise:
    • Clone b,
    • For each item in a:
      • If the item exists in our clone of b:
        • Remove the item from our clone of b,
      • Otherwise:
        • Return false.
    • Return true.

With Javascript 1.6, you can use every() and indexOf() to write:

function sameElements(a, b)
{
    if (a.length != b.length) {
        return false;
    }
    var ourB = b.concat();
    return a.every(function(item) {
        var index = ourB.indexOf(item);
        if (index < 0) {
            return false;
        } else {
            ourB.splice(index, 1);
            return true;
        }
    });
}

Звярніце ўвагу, гэтая рэалізацыя не цалкам выканаць вашыя патрабаванні, таму што IndexOf() выкарыстоўвае строгае роўнасць ( === ) усярэдзіне. Калі вы сапраўды хочаце, ня строгае роўнасць ( == ), вам прыйдзецца пісаць ўнутраны цыкл замест гэтага.

2
дададзена
Хоць цалкам празрыстая, гэтая версія аказалася самай павольнай (гл JSPerf спасылкі ў тэме).
дададзена аўтар georg, крыніца
Гэта, здаецца, працуе, дзякуй, але бачыць мой каментар да @BrunoLM: мае масівы даволі вялікія і паўтарыў IndexOf/зрошчвання не выглядае асабліва эфектыўным. Ці ёсць лепшы спосаб?
дададзена аўтар georg, крыніца
Хмм, sameElements ([1,2], [1,2,3]) ??
дададзена аўтар georg, крыніца
@ Thg435, без падтрымкі рэальных мностваў, я не ведаю, калі мы можам атрымаць значна хутчэй. Гэтае пытанне тлумачыць, як ім пераймаць, і фактычныя наборы падтрымліваюцца ў Mozilla браўзэрах.
дададзена аўтар Frédéric Hamidi, крыніца
@ Thg435, праўда, што цалкам ад мяне вылузваўся. Тэст Даўжыня ў парадку. Я буду абнаўляць мой адказ.
дададзена аўтар Frédéric Hamidi, крыніца

Дзякуй усім, для абмену ідэямі! Я прыдумаў наступнае

function sameElements(a, b) {
    var hash = function(x) {
        return typeof x + (typeof x == "object" ? a.indexOf(x) : x);
    }
    return a.map(hash).sort().join() == b.map(hash).sort().join();
}

Гэта не самага хуткага рашэння , але ИМО, найбольш чытаны адзін да гэтага часу.

1
дададзена

Edit 2
1) Thanks to user2357112 for pointing out the Object.prototype.toString.call issue this also showed, the reason it was that fast, that it didn't consider Arrays ...

Я усталяваў код, ён павінен працаваць зараз :), на жаль, яго зараз у а 59ops/с на хром і 45ops/с на сл.

Fiddle and JSPerf is updated.

Edit
1) I fixed the code, it supports mutliple variables referencing the same Object now. A little bit slower than before, but still over 100ops/s on chrome.

2) I tried using a bitmask instead of an array to keep multiple positions of the objects, but its nearly 15ops/s slow

3) As pointed ot in the comments i forgot to reset tmp after [[get]] is called fixed the code, the fiddle, and the perf.


Так дзякуючы user2357112 з яго адказ іншы падыход Херас з дапамогай падліку

var sameElements = (function() {
    var f, of, objectFlagName;
    of = objectFlagName = "__pos";
    var tstr = function (o) {
        var t = typeof o;
        if (o === null)
            t = "null";
        return t
    };
    var types = {};
    (function() {
        var tmp = {};
        Object.defineProperty(types, tstr(1), {
            set: function (v) {
                if (f)
                    tmp[v] = -~tmp[v];
                else
                    tmp[v] = ~-tmp[v];
            },
            get: function() {
                var ret = 1;
                for (var k in tmp) {
                    ret &= !tmp[k];
                }
                tmp = {};
                return ret;
            }
        });
    })();
    (function() {
        var tmp = {};
        Object.defineProperty(types, tstr(""), {

            set: function (v) {
                if (f) {
                    tmp[v] = -~tmp[v];
                } else {

                    tmp[v] = ~-tmp[v];
                }
            },
            get: function() {
                var ret = 1;
                for (var k in tmp) {
                    ret &= !tmp[k];
                }
                tmp = {};                
                return ret;
            }
        });
    })();

    (function() {
        var tmp = [];
        function add (v) {
            tmp.push(v);
            if (v[of]===undefined) {
                v[of] = [tmp.length -1];
            } else {
                v[of].push(tmp.length -1)
            }            

        }
        Object.defineProperty(types, tstr({}), {
            get: function() {
                var ret = true;
                for (var i = tmp.length - 1; i >= 0; i--) {
                    var c = tmp[i]
                    if (typeof c !== "undefined") {
                        ret = false
                        delete c[of]
                    }
                }
                tmp = [];
                return ret;
            },
            set: function (v) {
                var pos;
                if (f) {
                    add (v);
                } else if (!f && (pos = v[of]) !== void 0) {
                       tmp[pos.pop()] = undefined;
                       if (pos.length === 0)
                            delete v[of];
                } else {
                        add (v);
                }
            }
        });
    }());
    (function() {
        var tmp = 0;
        Object.defineProperty(types, tstr(undefined), {
            get: function() {
                var ret = !tmp;
                tmp = 0;
                return ret;

            },
            set: function() {
                tmp += f ? 1 : -1;
            }
        });
    })();
    (function() {
        var tmp = 0;
        Object.defineProperty(types, tstr(null), {
            get: function() {
                var ret = !tmp;
                tmp = 0;
                return ret;
            },
            set: function() {
                tmp += f ? 1 : -1;
            }
        });
    })();

    var tIt = [tstr(1), tstr(""), tstr({}), tstr(undefined), tstr(null)];

    return function eq(a, b) {

        f = true;
        for (var i = a.length - 1; i >= 0; i--) {
            var v = a[i];
            types[tstr(v)] = v;
        }
        f = false;
        for (var k = b.length - 1; k >= 0; k--) {
            var w = b[k];
            types[tstr(w)] = w;
        }
        var r = 1;
        for (var l = 0, j; j = tIt[l]; l++) {
            r &= types [j]
        }

        return !!r;
    }
    })()

Here is a JSFiddle and a JSPerf (it uses the same Arrays a and b as in the previous answers perf) with this code vs the Closure compiled

Heres the output. note: it doesn't support a deep comparison anymore, as is

var foo = {a:2}    
var bar = {a:1};
var a = [1, 2, foo, 2, bar, 2];
var b = [foo, 2, 2, 2, bar, 1];
var c = [bar, 2, 2, 2, bar, 1];
console.log(sameElements([NaN],[NaN])); //true
console.log (sameElements ( a,b))  //true
console.log (sameElements (b,c))   //false
1
дададзена
Там павінна быць чысты спосабам закадаваць гэта. Гэты код можа любіць толькі яго маці. Калі ласка, рэарганізаваць яго :)
дададзена аўтар Gili, крыніца
Акрамя таго, што, калі вам трэба выклікаць гэта двойчы? Усе вашыя структуры дадзеных будуць пастаянна пераблыталіся пасля першага званка, асабліва калі яна вяртаецца ілжывай ў першы раз.
дададзена аўтар user2357112, крыніца
<�Код> Object.prototype.toString.call (акно) === "[аб'ект глабальнай]" на маёй машыне. Я не думаю, што яго выкарыстанне з'яўляецца добрай ідэяй. См stackoverflow.com/a/4222755/2357112
дададзена аўтар user2357112, крыніца
@Gili Праўда, што :) Дзякуй за ўказанне, што, я буду рабіць, калі я знаходжу трохі разрэджанае час :)
дададзена аўтар C5H8NNaO4, крыніца
@ User2357112 Упс я цалкам Мент скінуць TMP кожны раз у паглынальнік, дзякуй =) я палатаю
дададзена аўтар C5H8NNaO4, крыніца
Я ў цяперашні час на мабільным тэлефоне, але я заўважыў, у аб'ектах тыпу сетэр, я павінен змяніць __ поз вар быць масівам, замест таго, каб падтрымаць Вар спасылкі на той жа аб'ект, я буду абнаўляць адказ адпаведна
дададзена аўтар C5H8NNaO4, крыніца
дададзены фрагмент да Brunos JSPerf , а таксама; Выбег з батарэі не можа запусціць яго
дададзена аўтар C5H8NNaO4, крыніца

я не быў упэўнены, што калі «===» у парадку, пытанне крыху vauge ... калі так, то гэта зусім трохі хутчэй і прасцей, чым іншыя магчымыя спосабы зрабіць гэта:

function isSame(a,b){
  return a.length==b.length && 
      a.filter(function(a){ return b.indexOf(a)!==-1 }).length == b.length;
}
1
дададзена

Выкарыстанне эфектыўных пошукавых табліц для падліку элементаў:

function sameElements(a) {//can compare any number of arrays
    var map, maps = [],//counting booleans, numbers and strings
        nulls = [],//counting undefined and null
        nans = [],//counting nans
        objs, counts, objects = [],
        al = arguments.length;

   //quick escapes:
    if (al < 2)
        return true;
    var l0 = a.length;
    if ([].slice.call(arguments).some(function(s) { return s.length != l0; }))
        return false;

    for (var i=0; i -1)
                    counts[ind]++;
                else
                    objs.push(val), counts.push(1);
            } else {//booleans, strings and numbers do compare together
                if (typeof val == "boolean")
                    val = +val;
                if (val in map)
                    map[val]++;
                else
                    map[val] = 1;
            }
        }
    }

   //testing if nulls and nans are the same everywhere
    for (var i=1; i

    Ён па-ранейшаму выкарыстоўвае IndexOf пошук сярод усіх аб'ектаў, так што калі ў вас ёсць мультинаборы з многімі рознымі аб'ектамі вы можаце аптымізаваць гэтую частку, а таксама. Паглядзіце на Унікальны ідэнтыфікатар ці аб'ект подпіс (і гэта паўтараюцца пытанні) аб тым, як атрымаць ключы пошуку табліцы для іх. І калі ў вас няма шмат прымітыўных значэнняў у мультимножествах, вы можаце проста захоўваць іх у масівах і сартаванні тым перад параўнаннем кожнага элемента за пунктам (як @Bruno зрабіў).

    Адмова ад адказнасці: Гэта рашэнне не спрабаваць атрымаць [[PrimitiveValue]] аб'ектаў, яны ніколі не будуць лічыцца роўным прымітываў (у той час як == будзе рабіць).

    Here is the update on @Bruno's jsperf test of the answers, yet I guess only two objects (each of them present 500 times in the 10k array) and no duplicate primitive values are not representative.

0
дададзена