Вопрос

Каков наиболее краткий и эффективный способ узнать, содержит ли массив JavaScript объект?

Это единственный известный мне способ сделать это:

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

Есть ли лучший и более краткий способ сделать это?

Это очень тесно связано с вопросом о переполнении стека. Лучший способ найти элемент в массиве JavaScript? который занимается поиском объектов в массиве с помощью indexOf.

Это было полезно?

Решение

Современные браузеры имеют Array#includes, что делает точно что, широко поддерживается, и имеет полифилл для старых браузеров.

> ['joe', 'jane', 'mary'].includes('jane');
true 

Вы также можете использовать Array#indexOf, что менее прямолинейно, но не требует использования Polyfills для устаревших браузеров.

предложения jQuery $.inArray, что функционально эквивалентно Array#indexOf.

underscore.js, служебная библиотека JavaScript, предлагает _.contains(list, value), псевдоним _.include(list, value), оба из которых используют индекс внутренне, если передан массив JavaScript.

Некоторые другие фреймворки предлагают аналогичные методы:

Обратите внимание, что некоторые платформы реализуют это как функцию, а другие добавляют функцию в прототип массива.

Другие советы

Обновление. Как упоминается в комментариях @orip, связанный тест был выполнен в 2008 году, поэтому результаты могут быть неактуальными для современных браузеров. Тем не менее, вам, вероятно, понадобится это для поддержки не современных браузеров, и они, вероятно, с тех пор не обновлялись. Всегда проверяйте себя.

Как уже говорили другие, итерация по массиву, вероятно, является наилучшим способом, но она была доказал, что уменьшающийся цикл while является самым быстрым способом итерации в JavaScript. Поэтому вы можете переписать свой код следующим образом:

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

Конечно, вы можете также расширить прототип Array:

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

indexOf возможно, но это «JavaScript-расширение стандарта ECMA-262»;как таковой он может отсутствовать в других реализациях стандарта».

Пример:

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

АФАИКС Microsoft делает нет предложите какую-то альтернативу к этому, но вы можете добавить аналогичную функциональность к массивам в Internet Explorer (и других браузерах, которые не поддерживают indexOf), если вы хотите, как быстрый поиск в Google показывает (например, Вот этот).

ECMAScript 7 представляет массив. prototype.includes .

Его можно использовать так:

[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 алгоритм равенства. Это означает, что вы можете определить, содержит ли массив NaN :

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

Кроме того, в отличие от indexOf , includes не пропускает пропущенные индексы:

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

В настоящее время это все еще черновик, но он может быть заполнил , чтобы он работал во всех браузерах.

b - это значение, а a - это массив. Возвращает true или false :

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

Верхние ответы предполагают примитивные типы, но если вы хотите узнать, содержит ли массив объект с некоторой чертой, 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

Вот совместимая с JavaScript 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;
        };
}

Использовать:

function isInArray(array, search)
{
    return array.indexOf(search) >= 0;
}

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}

Расширение объекта JavaScript Array - очень плохая идея, потому что вы вводите новые свойства (ваши пользовательские методы) в циклы for-in , которые могут нарушать существующие сценарии. Несколько лет назад авторам библиотеки прототипов пришлось заново создать свою библиотечную реализацию, чтобы удалить только такого рода вещи.

Если вам не нужно беспокоиться о совместимости с другим JavaScript, работающим на вашей странице, воспользуйтесь этим, в противном случае я бы порекомендовал более неуклюжее, но более безопасное автономное функциональное решение.

Подумав на секунду, если вы делаете этот вызов много раз, гораздо эффективнее использовать ассоциативный массив Map для поиска с использованием хеш-функции.

https://developer.mozilla.org/ EN-US / Docs / Web / JavaScript / Справка / Global_Objects / Карта

Один лайнер:

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

Я использую следующее:

Array.prototype.contains = function (v) {
    return this.indexOf(v) > -1;
}

var a = [ 'foo', 'bar' ];

a.contains('foo'); // true
a.contains('fox'); // false
function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

Array.prototype.some () был добавлен к стандарту ECMA-262 в 5-м издании

Надеемся, более быстрая двунаправленная indexOf / lastIndexOf альтернатива

2015

В то время как новый метод включает очень хорошо, поддержка в основном нулевая.

Давно я думал о том, как заменить медленные функции indexOf / lastIndexOf.

Эффективный способ уже найден, глядя на лучшие ответы. Из тех, что я выбрал, функция содержит , опубликованная @Damir Zekic, должна быть самой быстрой. Но в нем также говорится, что показатели взяты с 2008 года и поэтому устарели.

Я также предпочитаю while вместо for , но по непонятной причине я закончил написание функции с помощью цикла for. Это также можно сделать с помощью while - .

Мне было любопытно, если итерация была бы намного медленнее, если я проверяю обе стороны массива, делая это. По-видимому, нет, и поэтому эта функция примерно в два раза быстрее, чем те, которые проголосовали сверху. Очевидно, что это также быстрее, чем родной. Это в реальной среде, где вы никогда не узнаете, находится ли искомое значение в начале или в конце массива.

Когда вы знаете, что вы только что выдвинули массив со значением, использование lastIndexOf остается, вероятно, лучшим решением, но если вам нужно перемещаться по большим массивам и результат может быть везде, это может быть надежным решением для ускорения работы.

Двунаправленный indexOf / 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

В качестве теста я создал массив из 100 тыс. записей.

Три запроса: в начале, в середине & amp; в конце массива.

Надеюсь, вы тоже найдете это интересным и протестируете производительность.

Примечание. Как видите, я немного изменил функцию contains , чтобы отразить indexOf & amp; Вывод 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

Если вы используете JavaScript 1.6 или новее (Firefox 1.5 или новее), вы можете использовать Array.indexOf . В противном случае, я думаю, вы получите что-то похожее на ваш оригинальный код.

Если вы неоднократно проверяете наличие объекта в массиве, вам, возможно, стоит заглянуть в

<Ол>
  • Сохранение сортировки массива всегда путем сортировки вставок в вашем массиве (добавьте новые объекты в нужном месте)
  • Сделать обновление объектов как удаление + сортировка операции вставки и
  • Используйте бинарный поиск в вашем содержит (a, obj) .
  • function inArray(elem,array)
    {
        var len = array.length;
        for(var i = 0 ; i < len;i++)
        {
            if(array[i] == elem){return i;}
        }
        return -1;
    } 
    

    Возвращает индекс массива, если найден, или -1, если не найден

    Мы используем этот фрагмент (работает с объектами, массивами, строками):

    /*
     * @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
    

    Используйте некоторую функцию от lodash.

    Он лаконичен, точен и имеет отличную межплатформенную поддержку.

    Принятый ответ даже не соответствует требованиям.

    Требования. Рекомендуйте наиболее краткий и эффективный способ выяснить, содержит ли массив JavaScript объект.

    Принятый ответ:

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

    Моя рекомендация:

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

    Примечания.

    $. inArray отлично работает для определения того, существует ли значение скаляр в массиве скаляров ...

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

    ... но вопрос явно требует эффективного способа определить, содержится ли объект в массиве.

    Чтобы обрабатывать как скаляры, так и объекты, вы можете сделать это:

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

    Решение, которое работает во всех современных браузерах:

    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 функция с оптимизацией производительности.Спасибо путешествие за то, что указал на это.

    Пока array.indexOf(x)!=-1 является наиболее кратким способом сделать это (и уже более десяти лет поддерживается браузерами, не относящимися к Internet Explorer...), это не O(1), а скорее O(N), что ужасно.Если ваш массив не будет меняться, вы можете преобразовать его в хеш-таблицу, а затем выполнить table[x]!==undefined или ===undefined:

    Array.prototype.toTable = function() {
        var t = {};
        this.forEach(function(x){t[x]=true});
        return t;
    }
    

    Демо:

    var toRemove = [2,4].toTable();
    [1,2,3,4,5].filter(function(x){return toRemove[x]===undefined})
    

    (К сожалению, хотя вы можете создать Array.prototype.contains, чтобы «заморозить» массив и сохранить хеш-таблицу в this._cache в двух строках, это даст неверные результаты, если вы решите отредактировать массив позже.В JavaScript недостаточно ловушек, позволяющих сохранять это состояние, в отличие, например, от Python.)

    ECMAScript 6 предлагает элегантное предложение по поиску.

      

    Метод find выполняет функцию обратного вызова один раз для каждого элемента   присутствует в массиве, пока не найдет тот, где обратный вызов возвращает истину   значение. Если такой элемент найден, find немедленно возвращает значение   этого элемента. В противном случае поиск возвращает неопределенный. обратный вызов   вызывается только для индексов массива, которым присвоены значения; Это   не вызывается для индексов, которые были удалены или которые никогда не   были назначены значения.

    Вот документация по 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;
        }
      });
    }
    

    Использовать:

    var myArray = ['yellow', 'orange', 'red'] ;
    
    alert(!!~myArray.indexOf('red')); //true
    

    Демо

    Чтобы точно знать, что tilde ~ сделать на этом этапе, обратитесь к этому вопросу Что делает тильда, когда она предшествует выражению?.

    Использовать:

    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, я думаю, это максимально приближенный вариант для сравнения двух объектов в массиве.Кроме того, расширение объекта Array может быть неразумным решением, но иногда это нормально (если вы знаете об этом и о компромиссе).

    Можно использовать набор , который имеет Метод "имеет ()":

    function contains(arr, obj) {
      var proxy = new Set(arr);
      if (proxy.has(obj))
        return true;
      else
        return false;
    }
    
    var arr = ['Happy', 'New', 'Year'];
    console.log(contains(arr, 'Happy'));
    

    Вы также можете использовать этот трюк:

    var arrayContains = function(object) {
      return (serverList.filter(function(currentObject) {
        if (currentObject === object) {
          return currentObject
        }
        else {
          return false;
        }
      }).length > 0) ? true : false
    }
    
    <Ол>
  • Либо используйте Array.indexOf (Object) .
  • В ECMA 7 можно использовать Array.includes (Object) .
  • В ECMA 6 вы можете использовать Array.find (FunctionName) , где FunctionName является пользователем определенная функция для поиска объекта в массиве.

    Надеюсь, это поможет!

  • Похожее:Находит первый элемент с помощью «лямбды поиска»:

    Array.prototype.find = function(search_lambda) {
      return this[this.map(search_lambda).indexOf(true)];
    };
    

    Использование:

    [1,3,4,5,8,3,5].find(function(item) { return item % 2 == 0 })
    => 4
    

    То же самое в кофескрипте:

    Array.prototype.find = (search_lambda) -> @[@map(search_lambda).indexOf(true)]
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top