jquery تحميل - كيفية التحقق من وجود صفيفين متساويين مع JavaScript؟




مكتبة شرح (14)

هذا السؤال لديه بالفعل إجابة هنا:

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 أي طريقة لهذا؟


Answers

ليس لدى jQuery طريقة لمقارنة المصفوفات. ومع ذلك ، فإن مكتبة Underscore (أو مكتبة Lodash المماثلة) لديها مثل هذه الطريقة: isEqual ، ويمكنها التعامل مع مجموعة متنوعة من الحالات الأخرى (مثل حرفية الكائن). للتشبث بالمثال المقدم:

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

alert(_.isEqual(a, b) + "|" + _.isEqual(b, c));

بالمناسبة: تحتل Underscore الكثير من الطرق الأخرى التي تفتقدها jQuery أيضًا ، لذا فهي مكملة رائعة لـ jQuery.

تحرير: كما هو موضح في التعليقات ، ما ورد أعلاه الآن يعمل فقط إذا كان كلا المصفوفات عناصرها في نفس الترتيب ، أي:

_.isEqual([1,2,3], [1,2,3]); // true
_.isEqual([1,2,3], [3,2,1]); // false

لحسن الحظ ، تحتوي Javascript على طريقة مضمنة لحل هذه المشكلة بالتحديد ، sort :

_.isEqual([1,2,3].sort(), [3,2,1].sort()); // true

بالنسبة للقيم البدائية مثل الأرقام والسلاسل ، يعد هذا الحل سهلاً:

a = [1,2,3]

b = [3,2,1]

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

ستضمن الدعوة إلى sort() أن ترتيب العناصر لا يهم. ستقوم استدعاء toString() بإنشاء سلسلة بقيم مفصولة بفاصلة بحيث يمكن اختبار كلا السلاسل من أجل المساواة.


لا يوجد طريقه سهله لفعل ذلك. كنت بحاجة لذلك أيضاً ، لكني أردت وظيفة يمكنها أن تأخذ أي متغيرين واختبار للمساواة. يتضمن القيم غير الكائن والكائنات والصفائف وأي مستوى من التعشيش.

في سؤالك ، تشير إلى الرغبة في تجاهل ترتيب القيم في مصفوفة. الحل الخاص بي لا يقوم بذلك بطبيعته ، ولكن يمكنك تحقيقه عن طريق فرز المصفوفات قبل المقارنة بالمساواة

أردت أيضًا خيار صب الأشياء غير السلاسل حتى [1،2] === ["1" ، 2]

بما أن مشروعي يستخدم UnderscoreJs ، فقد قررت أن أجعله مزيجًا بدلاً من وظيفة مستقلة.

يمكنك اختبار ذلك على 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);

استنادًا إلى answer تيم جيمس وتعليق 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.6 من جافا سكريبت ، يصبح الأمر سهلاً كما يلي:

Array.prototype.equals = function( array ) {
  return this.length == array.length && 
         this.every( function(this_i,i) { return this_i == array[i] } )  
  }

على سبيل المثال ، [].equals([]) true ، بينما ينتج [1,2,3].equals( [1,3,2] ) false .


حتى لو بدا ذلك سهلاً للغاية ، فقد يكون مفيدًا في بعض الأحيان. إذا كان كل ما تحتاج إليه هو معرفة ما إذا كان هناك صفيفان يحتويان على نفس العناصر وهما بنفس الترتيب ، فجرّب ذلك:

[1, 2, 3].toString() == [1, 2, 3].toString()
true
[1, 2, 3,].toString() == [1, 2, 3].toString()
true
[1,2,3].toString() == [1, 2, 3].toString()
true

ومع ذلك ، لا يعمل هذا مع الحالات المتقدمة في الوضع مثل:

[[1,2],[3]].toString() == [[1],[2,3]].toString()
true

ذلك يعتمد على ما تحتاجه.


تمتص هذه الطريقة ، لكني تركتها هنا للرجوع إليها حتى يتجنب الآخرون هذا المسار:

استخدام الخيار 1 منninjagecko كان أفضل بالنسبة لي:

Array.prototype.equals = function(array) {
    return array instanceof Array && JSON.stringify(this) === JSON.stringify(array) ;
}

a = [1, [2, 3]]
a.equals([[1, 2], 3]) // false
a.equals([1, [2, 3]]) // true

كما سيتعامل مع حالة خالية وغير معروفة ، لأننا نضيف هذا إلى النموذج الأولي للمصفوفة والتحقق من أن الوسيطة الأخرى هي أيضًا مصفوفة.


هذا هو ما يجب عليك القيام به. من فضلك لا تستخدم 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.

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

باستخدام map() reduce() :

function arraysEqual (a1, a2) {
    return a1 === a2 || (
        a1 !== null && a2 !== null &&
        a1.length === a2.length &&
        a1
            .map(function (val, idx) { return val === a2[idx]; })
            .reduce(function (prev, cur) { return prev && cur; }, true)
    );
}

التعامل مع كل الأشياء الممكنة وحتى الإشارة نفسها في بنية الكائن. يمكنك رؤية المثال في نهاية الرمز.

var deepCompare = (function() {
    function internalDeepCompare (obj1, obj2, objects) {
        var i, objPair;

        if (obj1 === obj2) {
            return true;
        }

        i = objects.length;
        while (i--) {
            objPair = objects[i];
            if (  (objPair.obj1 === obj1 && objPair.obj2 === obj2) ||
                  (objPair.obj1 === obj2 && objPair.obj2 === obj1)  ) {                          
                return true;
            }                    
        }
        objects.push({obj1: obj1, obj2: obj2});

        if (obj1 instanceof Array) {
            if (!(obj2 instanceof Array)) {
                return false;
            }

            i = obj1.length;

            if (i !== obj2.length) {
               return false; 
            }

            while (i--) {
                if (!internalDeepCompare(obj1[i], obj2[i], objects)) {
                    return false;
                }
            }
        }
        else {
            switch (typeof obj1) {
                case "object":                
                    // deal with null
                    if (!(obj2 && obj1.constructor === obj2.constructor)) {
                        return false;
                    }

                    if (obj1 instanceof RegExp) {
                        if (!(obj2 instanceof RegExp && obj1.source === obj2.source)) {
                            return false;
                        }
                    }                 
                    else if (obj1 instanceof Date) {
                        if (!(obj2 instanceof Date && obj1.getTime() === obj2.getTime())) {
                            return false;
                        }
                    } 
                    else {    
                        for (i in obj1) {
                            if (obj1.hasOwnProperty(i)) {       
                                if (!(obj2.hasOwnProperty(i) && internalDeepCompare(obj1[i], obj2[i], objects))) {
                                    return false;
                                }
                            }
                        }         
                    }
                    break;
                case "function": 
                    if (!(typeof obj2 === "function" && obj1+"" === obj2+"")) {
                        return false;
                    }
                    break;
                default:                 //deal with NaN 
                    if (obj1 !== obj2 && obj1 === obj1 && obj2 === obj2) {
                        return false;            
                    }
            }
        }

        return true;
    }

    return function (obj1, obj2) {
        return internalDeepCompare(obj1, obj2, []);    
    };
}());

/*    
var a = [a, undefined, new Date(10), /.+/, {a:2}, function(){}, Infinity, -Infinity, NaN, 0, -0, 1, [4,5], "1", "-1", "a", null],
    b = [b, undefined, new Date(10), /.+/, {a:2}, function(){}, Infinity, -Infinity, NaN, 0, -0, 1, [4,5], "1", "-1", "a", null];
deepCompare(a, b);
*/

إذا كنت تستخدم lodash ولا تريد تعديل أي من الصفيف ، يمكنك استخدام الدالة _.xor () . يقارن صفيفتين كمجموعات ويعيد المجموعة التي تحتوي على الاختلاف. إذا كان طول هذا الاختلاف صفراً ، فإن الصفيفتين متساويان جوهرياً:

var a = [1, 2, 3];
var b = [3, 2, 1];
var c = new Array(1, 2, 3);
_.xor(a, b).length === 0
true
_.xor(b, c).length === 0
true

الخيار 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);
}

( قد لا يعمل هذا إذا احتوى المصفوفة على كائنات. يعتمد ما إذا كان هذا مازال يعمل مع الكائنات على ما إذا كان تنفيذ JSON يفرز المفاتيح أم لا. على سبيل المثال ، قد يساوي JSON {1:2,3:4} أو لا يساوي {3:4,1:2} ؛ هذا يعتمد على التنفيذ ، والمواصفات لا تقدم أي ضمان على الإطلاق. [2017 update: في الواقع تضمن مواصفات ES6 الآن مفاتيح الكائن بالترتيب الذي تم إدراجه بها. إذا كانت JSON.stringify application بعد ذلك ، ستتم إضافة كائنات متساوية إلى قيم متساوية. يجب إجراء المزيد من الأبحاث.] على الأقل على Chrome ، تميل وظيفة JSON.stringify إلى إرجاع المفاتيح بالترتيب الذي تم تعريفه به (على الأقل ما لاحظته) ، ولكن هذا السلوك الخاضع للتغيير في أي نقطة ولا ينبغي الاعتماد عليه ، وإذا اخترت عدم استخدام الكائنات في قوائمك ، فيجب أن يعمل هذا بشكل جيد. إذا كان لديك كائنات في قائمتك تحتوي جميعها على معرف فريد ، فيمكنك إجراء a1.map(function(x)}{return {id:x.uniqueId}}) إذا كان لديك كائنات عشوائية في قائمتك ، يمكنك القراءة للخيار رقم 2.)

هذا يعمل للصفائف المتداخلة كذلك.

ومع ذلك ، فهي غير فعالة بعض الشيء بسبب النفقات العامة لإنشاء هذه السلاسل وجمع القمامة.

الخيار 2

خيار "مناسب" أكثر ، والذي يمكنك تجاوزه للتعامل مع الحالات الخاصة (مثل الأشياء العادية والكائنات الخالية / غير المعروفة والعرف ، إذا كنت ترغب في ذلك):

// 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);
}

عرض:

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

لاستخدام هذا مثل وظيفة عادية ، قم بما يلي:

function allDeepEqual2() {
    return allDeepEqual([].slice.call(arguments));
}

عرض:

allDeepEqual2([[1,2],3], [[1,2],3])
true

خيارات 3

تحرير : إنه عام 2016 وكانت إجابتي السابقة المعقدة تنصت لي. إن هذا التطبيق العودي "للبرمجة المتكررة العودية 101" يبقي الكود بسيطًا جدًا ، ويفشل أيضًا في أقرب نقطة ممكنة (يمنحنا الكفاءة). كما أنه لا يولد بيانات سطحية زائدة عن الحاجة (ليس هناك أي خطأ في البرمجة الوظيفية بشكل عام ، ولكن فقط إبقائها نظيفة هنا).

إذا أردنا تطبيق ذلك على صفائف غير فارغة من المصفوفات ، فيمكننا إجراء تسلسل OutRrays.reduce (arraysEqual).

هذه هي وظيفتها الخاصة ، بدلا من استخدام Object.defineProperties لإرفاق Array.prototype ، لأن ذلك من شأنه أن يفشل مع خطأ رئيسي إذا مررنا في قيمة غير محددة (وهذا هو قرار التصميم الجيد إذا كنت ترغب في القيام بذلك) .

هذا فقط يجيب على سؤال أصلي OP.

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
    }
}

أمثلة:

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

إذا كنت تريد تطبيق ذلك على هياكل بيانات تشبه JSON مع كائنات js ، فيمكنك القيام بذلك. لحسن الحظ أننا نضمن أن تكون جميع مفاتيح الكائنات فريدة من نوعها ، لذا قم بالتكرار فوق الكائنات OwnProperties وفرزها حسب المفتاح ، ثم أكد أن كلا من مصفوفة المفاتيح التي تم فرزها متساوية وأن متساوية القيمة متساوية ، ومجرد تكراري. يمكننا توسيع هذا ليشمل الخرائط أيضًا (حيث تكون المفاتيح فريدة أيضًا). (ومع ذلك ، إذا قمنا بتوسيع نطاق ذلك إلى مجموعات ، فإننا نواجه مشكلة تشابه الشجرة http://logic.pdmi.ras.ru/~smal/files/smal_jass08_slides.pdf - لحسن الحظ ، ليس من الصعب تحديد شكل التماثل العام ؛ يوجد في الواقع ، خوارزمية O (#vertices) لحلها ، ولكن يمكن أن تكون معقدة للغاية للقيام بذلك بكفاءة ، الحالة المرضية هي إذا كان لديك مجموعة تتكون من الكثير من الأشياء التي تبدو غير قابلة للتمييز ، ولكن عند إجراء المزيد من الفحص ، فإن بعض هذه الكائنات قد تختلف عندما تتعمق أكثر في ذلك ، ويمكنك أيضًا حل هذه المشكلة باستخدام التجزئة لرفض جميع الحالات تقريبًا.)

الخيار 4: (استمرار تعديل 2016)

يجب أن يعمل هذا مع معظم الكائنات:

function deepEquals(a,b) {
    if (a instanceof Array && b instanceof Array)
        return arraysEqual(a,b);
    if (Object.getPrototypeOf(a)===Object.prototype && Object.getPrototypeOf(b)===Object.prototype)
        return objectsEqual(a,b);
    if (a instanceof Map && b instanceof Map)
        return mapsEqual(a,b);        
    if (a instanceof Set && b instanceof Set)
        throw "Error: set equality by hashing not implemented."
    if ((a instanceof ArrayBuffer || ArrayBuffer.isView(a)) && (b instanceof ArrayBuffer || ArrayBuffer.isView(b)))
        return typedArraysEqual(a,b);
    return a==b;  // see note[1]
}

function arraysEqual(a,b) {
    if (a.length!=b.length)
        return false;
    for(var i=0; i<a.length; i++)
        if (!deepEquals(a[i],b[i]))
            return false;
    return true;
}
function objectsEqual(a,b) {
    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 (aKeys[i]!=bKeys[i]) // keys must be strings
            return false;
    return deepEquals(aKeys.map(k=>a[k]), aKeys.map(k=>b[k]));
}
function mapsEqual(a,b) {
    if (a.size!=b.size)
        return false;
    var aPairs = Array.from(a);
    var bPairs = Array.from(b);
    aPairs.sort((x,y) => x[0]<y[0]);
    bPairs.sort((x,y) => x[0]<y[0]);
    for(var i=0; i<a.length; i++)
        if (!deepEquals(aPairs[i][0],bPairs[i][0]) || !deepEquals(aPairs[i][1],bPairs[i][1]))
            return false;
    return true;
}
function typedArraysEqual(a,b) {
    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;
}

العرض التوضيحي (لم يتم اختباره على نطاق واسع):

var nineTen = new Float32Array(2);
nineTen[0]=9; nineTen[1]=10;
deepEquals(
    [[1,[2,3]], 4, {a:5,b:6}, new Map([['c',7],['d',8]]), nineTen],
    [[1,[2,3]], 4, {b:6,a:5}, new Map([['d',8],['c',7]]), nineTen]
)

(sidenote: الخرائط عبارة عن قواميس es6 لا أستطيع أن أعرف ما إذا كان لديهم أداء بحث O (1) أو O (log (N)) ، ولكن في أي حال يتم "ترتيبهم" بمعنى أنهم يتتبعون حيث تم إدراج أزواج القيمة الرئيسية فيها ، ومع ذلك ، فإن الدلالة ما إذا كان اثنان من الخرائط يجب أن يكونا متساويين إذا تم إدراج عناصر في ترتيب مختلف فيها ، أمرًا غامضًا ، فأنا أعطي عينة من التنفيذ أقل من عمق. حتى لو تم إدخال العناصر في ترتيب مختلف.)

(ملاحظة [1]: قد ترغب في تجاوز الخط المرصود بمفهوم مخصص للمساواة ، والذي سيتعين عليك أيضًا تغيير الوظائف الأخرى في أي مكان يظهر فيه. ================================================================================================

يجب أن تكون قادراً على توسيع أعلاه إلى WeakMaps ، WeakSets. لست متأكدًا مما إذا كان من المنطقي التمديد إلى DataViews. يجب أن يكون أيضًا قادرًا على التمديد إلى RegExps على الأرجح ، إلخ.

كلما قمت بتمديده ، تدرك أنك تقوم بالكثير من المقارنات غير الضرورية. هذا هو المكان الذي يمكن أن تكون فيه وظيفة type التي قمت بتحديدها بطريقة مبكرة (الحل رقم 2) مفيدة. ثم يمكنك إرسالها على الفور. ما إذا كان ذلك يستحق النفقات العامة (ربما؟ غير متأكد كيف يعمل تحت غطاء محرك السيارة) سلسلة تمثل نوع ما متروك لك. يمكنك بعد ذلك فقط إعادة كتابة المرسل ، أي وظيفة 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);
}

أنا استخدم هذا البرنامج النصي البسيط

$('input[name="myRadio"]').on('change', function() {
  var radioValue = $('input[name="myRadio"]:checked').val();        
  alert(radioValue); 
});




javascript jquery