arrays - كيف أتحقق مما إذا كان الصفيف يحتوي على كائن في JavaScript؟




browser (25)

ما هي الطريقة الأكثر إيجازًا وكفاءةً لمعرفة ما إذا كانت مصفوفة جافا سكريبت تحتوي على كائن؟

هذه هي الطريقة الوحيدة التي أعلم بها:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (a[i] === obj) {
            return true;
        }
    }
    return false;
}

هل هناك طريقة أفضل وأكثر إيجازًا لتحقيق ذلك؟

هذا يرتبط ارتباطًا وثيقًا بمسألة أفضل طريقة للعثور على عنصر في مصفوفة JavaScript؟ الذي يعالج الكائنات في صفيف باستخدام indexOf .


Answers

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

const items = [ {a: '1'}, {a: '2'}, {a: '3'} ]

items.some(item => item.a === '3')  // returns true
items.some(item => item.a === '4')  // returns false

الشيء الجميل في ذلك هو أن يتم إلغاء التكرار بمجرد العثور على عنصر بحيث يتم حفظ دورات التكرار غير الضرورية.

أيضا ، تناسبها بشكل جيد في بيان if لأنها ترجع قيمة منطقية:

if (items.some(item => item.a === '3')) {
  // do something
}

* كما أشار jamess في التعليق ، اعتبارا من اليوم ، سبتمبر 2018 ، Array.prototype.some() مدعوم بالكامل: جدول دعم caniuse.com


كما ذكر آخرون ، يمكنك استخدامه Array.indexOf، ولكنه غير متوفر في جميع المتصفحات. إليك الشفرة من https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf لجعلها تعمل بنفس الطريقة في المتصفحات القديمة.

يعد indexOf إضافة حديثة إلى معيار ECMA-262 ؛ على هذا النحو قد لا تكون موجودة في جميع المتصفحات. يمكنك حل هذه المشكلة من خلال إدراج الشفرة التالية في بداية نصوصك البرمجية ، مما يسمح باستخدام indexOf في التطبيقات التي لا تدعمها أصلاً. هذه الخوارزمية هي بالضبط تلك المحددة في ECMA-262 ، الطبعة الخامسة ، بافتراض أن قيمة Object ID و TypeError و Number و Math.floor و Math.abs و Math.max الخاصة بهم.

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
        "use strict";
        if (this == null) {
            throw new TypeError();
        }
        var t = Object(this);
        var len = t.length >>> 0;
        if (len === 0) {
            return -1;
        }
        var n = 0;
        if (arguments.length > 1) {
            n = Number(arguments[1]);
            if (n != n) { // shortcut for verifying if it's NaN
                n = 0;
            } else if (n != 0 && n != Infinity && n != -Infinity) {
                n = (n > 0 || -1) * Math.floor(Math.abs(n));
            }
        }
        if (n >= len) {
            return -1;
        }
        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
        for (; k < len; k++) {
            if (k in t && t[k] === searchElement) {
                return k;
            }
        }
        return -1;
    }
}

إذا كنت تستخدم JavaScript 1.6 أو أحدث (Firefox 1.5 أو أحدث) ، يمكنك استخدام Array.indexOf . بخلاف ذلك ، أعتقد أنك ستنتهي بشيء مماثل لرمزك الأصلي.


إليك تنفيذ متوافق مع جافا سكريبت 1.6 من Array.indexOf :

if (!Array.indexOf)
{
  Array.indexOf = [].indexOf ?
      function (arr, obj, from) { return arr.indexOf(obj, from); }:
      function (arr, obj, from) { // (for IE6)
        var l = arr.length,
            i = from ? parseInt( (1*from) + (from<0 ? l:0), 10) : 0;
        i = i<0 ? 0 : i;
        for (; i<l; i++) {
          if (i in arr  &&  arr[i] === obj) { return i; }
        }
        return -1;
      };
}

بأي حال من الأحوال أفضل ، ولكن كنت مجرد الحصول على الإبداع وإضافة إلى ذخيرة.

لا تستخدم هذا

Object.defineProperty(Array.prototype, 'exists', {
  value: function(element, index) {

    var index = index || 0

    return index === this.length ? -1 : this[index] === element ? index : this.exists(element, ++index)
  }
})


// Outputs 1
console.log(['one', 'two'].exists('two'));

// Outputs -1
console.log(['one', 'two'].exists('three'));

console.log(['one', 'two', 'three', 'four'].exists('four'));




آمل أسرع ثنائي الاتجاه indexOf / lastIndexOf البديل

2015

في حين أن الطريقة الجديدة array.includes(value) لطيفة للغاية ، فإن الدعم هو صفر في الأساس في الوقت الحالي.

لقد مضى وقت طويل كنت أفكر في طريقة لاستبدال وظائف indexOf / lastIndexOf البطيئة.

تم بالفعل العثور على طريقة أداء بالنظر إلى أفضل الإجابات. من تلك التي اخترتها contains وظيفة نشرهاDamir Zekic والتي يجب أن تكون أسرع. ولكنه يذكر أيضًا أن المعايير القياسية هي من عام 2008 ، لذا فهي قديمة.

أنا أيضا تفضل أكثر من for ، ولكن لسبب غير محدد انتهيت من كتابة وظيفة مع حلقة. ويمكن أيضا أن يتم مع فترة while -- .

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

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

مؤشر ثنائي الاتجاهOf / lastIndexOf

function bidirectionalIndexOf(a, b, c, d, e){
  for(c=a.length,d=c*1; c--; ){
    if(a[c]==b) return c; //or this[c]===b
    if(a[e=d-1-c]==b) return e; //or a[e=d-1-c]===b
  }
  return -1
}

//Usage
bidirectionalIndexOf(array,'value');

تجربة أداء

http://jsperf.com/bidirectionalindexof

كما اختبار أنا خلقت مصفوفة مع مدخلات 100K.

ثلاثة استعلامات: في البداية ، في المنتصف وفي نهاية المصفوفة.

آمل أن تجد هذا أيضًا مثيرًا للاهتمام وأختبر الأداء.

ملاحظة: كما ترى ، قمت بتعديل الدالة المحتوية بشكل طفيف لتعكس مخرجات indexOf & lastIndexOf (بحيث تكون true الأساس مع index و false مع -1 ). لا ينبغي أن يؤذي ذلك.

تشكيلة النموذج الأولية

Object.defineProperty(Array.prototype,'bidirectionalIndexOf',{value:function(b,c,d,e){
  for(c=this.length,d=c*1; c--; ){
    if(this[c]==b) return c; //or this[c]===b
    if(this[e=d-1-c] == b) return e; //or this[e=d-1-c]===b
  }
  return -1
},writable:false, enumerable:false});

// Usage
array.bidirectionalIndexOf('value');

يمكن أيضًا تعديل الوظيفة بسهولة لإرجاع true أو false أو حتى الكائن أو السلسلة أو أيًا كانت.

وهنا هو البديل while :

function bidirectionalIndexOf(a, b, c, d){
  c=a.length; d=c-1;
  while(c--){
    if(b===a[c]) return c;
    if(b===a[d-c]) return d-c;
  }
  return c
}

// Usage
bidirectionalIndexOf(array,'value');

كيف يكون هذا ممكنا؟

أعتقد أن الحساب البسيط للحصول على مؤشر الانعكاس في صفيف بسيط لدرجة أنه أسرع مرتين من تكرار حلقة فعلية.

في ما يلي مثال معقد يقوم بإجراء ثلاث عمليات تحقق لكل تكرار ، ولكن هذا ممكن فقط مع حساب أطول يؤدي إلى تباطؤ في الشفرة.

http://jsperf.com/bidirectionalindexof/2


التحديث: كما يذكرorip في التعليقات ، تم إجراء الاختبار القياسي المرتبط في عام 2008 ، لذا قد لا تكون النتائج ذات صلة بالمتصفحات الحديثة. ومع ذلك ، ربما تحتاج إلى هذا لدعم المتصفحات غير الحديثة على أي حال وربما لم يتم تحديثها منذ ذلك الحين. اختبر دائما لنفسك.

كما قال الآخرون ، فإن التكرار عبر المصفوفة هو على الأرجح أفضل طريقة ، ولكن ثبت أن التراجع while التكرار هو أسرع طريقة للتكرار في JavaScript. لذلك قد تحتاج إلى إعادة كتابة التعليمات البرمجية كما يلي:

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

بالطبع ، يمكنك أيضًا توسيع النموذج الأولي للصفيفة:

Array.prototype.contains = function(obj) {
    var i = this.length;
    while (i--) {
        if (this[i] === obj) {
            return true;
        }
    }
    return false;
}

والآن يمكنك ببساطة استخدام ما يلي:

alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false

استخدام لواضع some الوظائف.

إنه موجز ودقيق ولديه دعم كبير عبر النظام الأساسي.

الإجابة المقبولة لا تفي بالمتطلبات.

المتطلبات: التوصية بأكثر الطرق اختصارًا وفعالية لمعرفة ما إذا كانت مصفوفة جافا سكريبت تحتوي على كائن.

الجواب المقبول:

$.inArray({'b': 2}, [{'a': 1}, {'b': 2}])
> -1

توصيتي:

_.some([{'a': 1}, {'b': 2}], {'b': 2})
> true

ملاحظات:

$ .inArray يعمل بشكل جيد لتحديد ما إذا كانت القيمة العددية موجودة في مصفوفة من scalars ...

$.inArray(2, [1,2])
> 1

... ولكن السؤال يسأل بوضوح عن طريقة فعالة لتحديد ما إذا كان كائن موجود في مصفوفة.

من أجل التعامل مع كلا العددين والكائنات ، يمكنك القيام بذلك:

(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)

b هي القيمة ، a المصفوفة. تقوم بإرجاع true أو false :

function(a, b) {
    return a.indexOf(b) != -1
}

ECMAScript 6 لديه اقتراح أنيق على العثور عليها.

تقوم طريقة البحث بتنفيذ وظيفة معاودة الاتصال مرة واحدة لكل عنصر موجود في المصفوفة حتى تعثر على وظيفة حيث تقوم معاودة الاتصال بإرجاع قيمة حقيقية. إذا تم العثور على هذا العنصر ، فابحث على الفور عن قيمة هذا العنصر. خلاف ذلك ، والعثور على إرجاع غير محدد. يتم استدعاء رد الاتصال فقط من أجل فهارس الصفيف التي لها قيم معينة؛ لا يتم استدعاء الفهارس التي تم حذفها أو التي لم يتم تعيين قيم لها.

هنا هو وثائق MDN على ذلك.

تعمل وظيفة البحث على هذا النحو.

function isPrime(element, index, array) {
    var start = 2;
    while (start <= Math.sqrt(element)) {
        if (element % start++ < 1) return false;
    }
    return (element > 1);
}

console.log( [4, 6, 8, 12].find(isPrime) ); // Undefined, not found
console.log( [4, 5, 8, 12].find(isPrime) ); // 5

يمكنك استخدام هذا في ECMAScript 5 وأدناه من خلال تعريف الوظيفة .

if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(predicate) {
      if (this == null) {
        throw new TypeError('Array.prototype.find called on null or undefined');
      }
      if (typeof predicate !== 'function') {
        throw new TypeError('predicate must be a function');
      }
      var list = Object(this);
      var length = list.length >>> 0;
      var thisArg = arguments[1];
      var value;

      for (var i = 0; i < length; i++) {
        if (i in list) {
          value = list[i];
          if (predicate.call(thisArg, value, i, list)) {
            return value;
          }
        }
      }
      return undefined;
    }
  });
}

الحل الذي يعمل في جميع المتصفحات الحديثة:

function contains(arr, obj) {
  const stringifiedObj = JSON.stringify(obj); // Cache our object to not call `JSON.stringify` on every iteration
  return arr.some(item => JSON.stringify(item) === stringifiedObj);
}

الاستعمال:

contains([{a: 1}, {a: 2}], {a: 1}); // true

حل IE6 +:

function contains(arr, obj) {
  var stringifiedObj = JSON.stringify(obj)
  return arr.some(function (item) {
    return JSON.stringify(item) === stringifiedObj;
  });
}

// .some polyfill, not needed for IE9+
if (!('some' in Array.prototype)) {
  Array.prototype.some = function (tester, that /*opt*/) {
    for (var i = 0, n = this.length; i < n; i++) {
      if (i in this && tester.call(that, this[i], i, this)) return true;
    } return false;
  };
}

الاستعمال:

contains([{a: 1}, {a: 2}], {a: 1}); // true

لماذا تستخدم JSON.stringify ؟

Array.indexOf و Array.includes (بالإضافة إلى معظم الإجابات هنا) فقط مقارنة حسب المرجع وليس حسب القيمة.

[{a: 1}, {a: 2}].includes({a: 1});
// false, because {a: 1} is a new object

علاوة

غير محسن ES6 واحد الخطوط الملاحية المنتظمة:

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

ملاحظة: مقارنة الأشياء حسب القيمة ستعمل بشكل أفضل إذا كانت المفاتيح بنفس الترتيب ، لكي تكون آمنًا ، يمكنك ترتيب المفاتيح أولاً باستخدام حزمة مثل هذه: https://www.npmjs.com/package/sort-keys

تحديث contains وظيفة مع تحسين perf. شكرا بفضل اشارة بها.


استعمال:

Array.prototype.contains = function(x){
  var retVal = -1;

  // x is a primitive type
  if(["string","number"].indexOf(typeof x)>=0 ){ retVal = this.indexOf(x);}

  // x is a function
  else if(typeof x =="function") for(var ix in this){
    if((this[ix]+"")==(x+"")) retVal = ix;
  }

  //x is an object...
  else {
    var sx=JSON.stringify(x);
    for(var ix in this){
      if(typeof this[ix] =="object" && JSON.stringify(this[ix])==sx) retVal = ix;
    }
  }

  //Return False if -1 else number if numeric otherwise string
  return (retVal === -1)?false : ( isNaN(+retVal) ? retVal : +retVal);
}

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


يقدم ECMAScript 7 array.includes(value) .

يمكن استخدامه على النحو التالي:

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false

كما أنه يقبل أيضًا وسيطة ثانية اختيارية من fromIndex :

[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true

على عكس indexOf ، والذي يستخدم SameValueZero صارمة للمساواة ، includes مقارنات باستخدام خوارزمية SameValueZero المساواة. وهذا يعني أنه بإمكانك اكتشاف ما إذا كانت مصفوفة تحتوي على NaN :

[1, 2, NaN].includes(NaN); // true

أيضًا بخلاف indexOf ، لا includes تخطي المؤشرات المفقودة:

new Array(5).includes(undefined); // true

لا يزال حاليًا مسودة ، ولكن يمكن polyfill لجعلها تعمل على جميع المتصفحات.


قد يكون indexOf ربما ، ولكنه "ملحق JavaScript إلى معيار ECMA-262 ؛ لذا فقد لا يكون موجودًا في تطبيقات أخرى للمعيار."

مثال:

[1, 2, 3].indexOf(1) => 0
["foo", "bar", "baz"].indexOf("bar") => 1
[1, 2, 3].indexOf(4) => -1

AFAICS لا تقدم Microsoft نوعًا بديلاً لهذا ، ولكن يمكنك إضافة وظائف مماثلة إلى الصفائف في Internet Explorer (والمتصفحات الأخرى التي لا تدعم indexOf ) إذا أردت ، كما يظهر بحث Google سريع (على سبيل المثال ، هذا واحد ).


إليك كيفية تنفيذ النموذج الأولي :

/**
 *  Array#indexOf(item[, offset = 0]) -> Number
 *  - item (?): A value that may or may not be in the array.
 *  - offset (Number): The number of initial items to skip before beginning the
 *      search.
 *
 *  Returns the position of the first occurrence of `item` within the array &mdash; or
 *  `-1` if `item` doesn't exist in the array.
**/
function indexOf(item, i) {
  i || (i = 0);
  var length = this.length;
  if (i < 0) i = length + i;
  for (; i < length; i++)
    if (this[i] === item) return i;
  return -1;
}

انظر أيضا here لكيفية ربطها.


يمكنك أيضًا استخدام هذه الحيلة:

var arrayContains = function(object) {
  return (serverList.filter(function(currentObject) {
    if (currentObject === object) {
      return currentObject
    }
    else {
      return false;
    }
  }).length > 0) ? true : false
}

إن توسيع كائن Array جافا سكريبت هو فكرة سيئة للغاية لأنك تقدم خصائص جديدة (طرقك المخصصة) for-in الحلقات التي يمكن أن تحطم البرامج النصية الموجودة. قبل بضع سنوات ، كان على مؤلفي مكتبة Prototype أن يعيدوا تصميم تطبيق المكتبة لإزالة هذا النوع من الأشياء.

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


function inArray(elem,array)
{
    var len = array.length;
    for(var i = 0 ; i < len;i++)
    {
        if(array[i] == elem){return i;}
    }
    return -1;
} 

إرجاع فهرس الصفيف إذا وجد ، أو إذا لم يتم العثور على -1


حسنًا ، يمكنك فقط تحسين رمزك للحصول على النتيجة! هناك العديد من الطرق للقيام بهذا الأمر الأكثر نظافة وأفضل ، لكني أردت فقط الحصول على النمط الخاص بك وتطبيقه على ذلك JSON.stringify، فقط قم ببساطة بشيء من هذا القبيل في حالتك:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (JSON.stringify(a[i]) === JSON.stringify(obj)) {
            return true;
        }
    }
    return false;
}

إذا كنت تتحقق مرارا وتكرارا لوجود كائن في مصفوفة ربما ينبغي النظر في

  1. الحفاظ على فرز المصفوفات في جميع الأوقات عن طريق القيام بفرز الإدراج في المصفوفة (ضع كائنات جديدة في المكان المناسب)
  2. جعل تحديث الكائنات كإزالة + عملية إدراج مفروز و
  3. استخدم بحث البحث الثنائي في الخاص بك contains(a, obj) .

الخطوط الملاحية المنتظمة واحد:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}

نستخدم هذا المقتطف (يعمل مع الكائنات ، المصفوفات ، السلاسل):

/*
 * @function
 * @name Object.prototype.inArray
 * @description Extend Object prototype within inArray function
 *
 * @param {mix}    needle       - Search-able needle
 * @param {bool}   searchInKey  - Search needle in keys?
 *
 */
Object.defineProperty(Object.prototype, 'inArray',{
    value: function(needle, searchInKey){

        var object = this;

        if( Object.prototype.toString.call(needle) === '[object Object]' || 
            Object.prototype.toString.call(needle) === '[object Array]'){
            needle = JSON.stringify(needle);
        }

        return Object.keys(object).some(function(key){

            var value = object[key];

            if( Object.prototype.toString.call(value) === '[object Object]' || 
                Object.prototype.toString.call(value) === '[object Array]'){
                value = JSON.stringify(value);
            }

            if(searchInKey){
                if(value === needle || key === needle){
                return true;
                }
            }else{
                if(value === needle){
                    return true;
                }
            }
        });
    },
    writable: true,
    configurable: true,
    enumerable: false
});

الاستعمال:

var a = {one: "first", two: "second", foo: {three: "third"}};
a.inArray("first");          //true
a.inArray("foo");            //false
a.inArray("foo", true);      //true - search by keys
a.inArray({three: "third"}); //true

var b = ["one", "two", "three", "four", {foo: 'val'}];
b.inArray("one");         //true
b.inArray('foo');         //false
b.inArray({foo: 'val'})   //true
b.inArray("{foo: 'val'}") //false

var c = "String";
c.inArray("S");        //true
c.inArray("s");        //false
c.inArray("2", true);  //true
c.inArray("20", true); //false

إذا كنت تبحث عن بديل لكتابة الشيك القبيح -1 ، فأنت تقترب من التيلدا بدلاً من ذلك.

if (~haystack.indexOf('needle')) alert('found');

جو زيمرمان - سترى أن استخدام ~ في -1 يحولها إلى 0. الرقم 0 هو قيمة خادعة ، بمعنى أنه سيتم تقييمه إلى خطأ عند تحويله إلى قيمة منطقية. قد لا يبدو ذلك بمثابة إحصاء كبير في البداية ، ولكن تذكر أن الدالات مثل indexOf ستعود -1 عندما لا يتم العثور على الاستعلام. هذا يعني أنه بدلاً من كتابة شيء مشابه لهذا:

if (someStr.indexOf("a") >= 0) {
  // Found it
} else  {
  // Not Found
}

يمكنك الآن الحصول على عدد أقل من الأحرف في شفرتك حتى تتمكن من كتابتها كما يلي:

if (~someStr.indexOf("a")) {
  // Found it
} else  {
  // Not Found
}

مزيد من التفاصيل هنا





javascript arrays browser