Лучший способ узнать, есть ли элемент в массиве JavaScript?

Каков наилучший способ найти объект в массиве?

Это лучший способ, которым я знаю:

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

include([1,2,3,4], 3); // true
include([1,2,3,4], 6); // undefined

8 ответов

Решение

Начиная с ECMAScript 2016 вы можете использовать includes()

arr.includes(obj);

Если вы хотите поддерживать IE или другие старые браузеры:

function include(arr,obj) {
    return (arr.indexOf(obj) != -1);
}

РЕДАКТИРОВАТЬ: Это не будет работать на IE6, 7 или 8, хотя. Лучший обходной путь - определить его самостоятельно, если его нет:

  1. Версия Mozilla (ECMA-262):

      if (!Array.prototype.indexOf)
      {
    
           Array.prototype.indexOf = function(searchElement /*, fromIndex */)
    
        {
    
    
        "use strict";
    
        if (this === void 0 || 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 > 0)
        {
          n = Number(arguments[1]);
          if (n !== n)
            n = 0;
          else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
            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;
      };
    
    }
    
  2. Версия Дэниела Джеймса:

    if (!Array.prototype.indexOf) {
      Array.prototype.indexOf = function (obj, fromIndex) {
        if (fromIndex == null) {
            fromIndex = 0;
        } else if (fromIndex < 0) {
            fromIndex = Math.max(0, this.length + fromIndex);
        }
        for (var i = fromIndex, j = this.length; i < j; i++) {
            if (this[i] === obj)
                return i;
        }
        return -1;
      };
    }
    
  3. версия roosteronacid:

    Array.prototype.hasObject = (
      !Array.indexOf ? function (o)
      {
        var l = this.length + 1;
        while (l -= 1)
        {
            if (this[l - 1] === o)
            {
                return true;
            }
        }
        return false;
      } : function (o)
      {
        return (this.indexOf(o) !== -1);
      }
    );
    

Если вы используете jQuery:

$.inArray(5 + 5, [ "8", "9", "10", 10 + "" ]);

Для получения дополнительной информации: http://api.jquery.com/jQuery.inArray/

Во-первых, реализовать indexOf в JavaScript для браузеров, у которых его еще нет. Например, см. Дополнения к массиву Эрика Арвидссона (также, связанный пост в блоге). И тогда вы можете использовать indexOf не беспокоясь о поддержке браузера. Вот немного оптимизированная версия его indexOf реализация:

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (obj, fromIndex) {
        if (fromIndex == null) {
            fromIndex = 0;
        } else if (fromIndex < 0) {
            fromIndex = Math.max(0, this.length + fromIndex);
        }
        for (var i = fromIndex, j = this.length; i < j; i++) {
            if (this[i] === obj)
                return i;
        }
        return -1;
    };
}

Он изменен для хранения длины, поэтому нет необходимости искать его на каждой итерации. Но разница не огромна. Функция менее общего назначения может быть быстрее:

var include = Array.prototype.indexOf ?
    function(arr, obj) { return arr.indexOf(obj) !== -1; } :
    function(arr, obj) {
        for(var i = -1, j = arr.length; ++i < j;)
            if(arr[i] === obj) return true;
        return false;
    };

Я предпочитаю использовать стандартную функцию и оставлять такую ​​микрооптимизацию, когда это действительно необходимо. Но если вы увлекаетесь микрооптимизацией, я адаптировал тесты, на которые ссылается roosterononacid в комментариях, к тестам поиска в массивах. Хотя они довольно грубые, полное расследование должно было бы проверить массивы разных типов, разной длины и найти объекты, которые встречаются в разных местах.

Если массив не отсортирован, лучшего способа не существует (кроме использования вышеупомянутого indexOf, который, я думаю, является тем же). Если массив отсортирован, вы можете выполнить бинарный поиск, который работает следующим образом:

  1. Выберите средний элемент массива.
  2. Элемент, который вы ищете, больше, чем элемент, который вы выбрали? Если это так, вы удалили нижнюю половину массива. Если это не так, вы устранили верхнюю половину.
  3. Выберите средний элемент оставшейся половины массива и продолжайте, как в шаге 2, удаляя половинки оставшегося массива. В конце концов вы либо найдете свой элемент, либо у вас не останется массив для просмотра.

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

[ ].has(obj)

при условии, .indexOf() реализуется

Object.defineProperty( Array.prototype,'has',
{
    value:function(o, flag){
    if (flag === undefined) {
        return this.indexOf(o) !== -1;
    } else {   // only for raw js object
        for(var v in this) {
            if( JSON.stringify(this[v]) === JSON.stringify(o)) return true;
        }
        return false;                       
    },
    // writable:false,
    // enumerable:false
})

!!! не сделать Array.prototype.has=function(){... потому что вы добавите перечислимый элемент в каждый массив, и js не работает.

//use like          
[22 ,'a', {prop:'x'}].has(12) // false
["a","b"].has("a") //  true

[1,{a:1}].has({a:1},1) // true
[1,{a:1}].has({a:1}) // false

использование 2-го аргумента (флага) заставляет сравнение по значению вместо ссылки

Это зависит от вашей цели. Если вы программируете для Интернета, избегайте indexOf, он не поддерживается Internet Explorer 6 (многие из них все еще используются!) или не используются условно:

if (yourArray.indexOf !== undefined) result = yourArray.indexOf(target);
else result = customSlowerSearch(yourArray, target);

indexOf вероятно, кодируется в нативном коде, поэтому это быстрее, чем все, что вы можете сделать в JavaScript (кроме бинарного поиска / дихотомии, если массив уместен). Примечание: это вопрос вкуса, но я бы сделал return false; в конце вашей рутины, чтобы вернуть истинное логическое значение...

Вот некоторые мета-знания для вас - если вы хотите знать, что вы можете сделать с Array, проверьте документацию - вот страница Array для Mozilla

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array

Там вы увидите ссылку на indexOf, добавленный в Javascript 1.6

Надежный способ проверить, является ли объект массивом в JavaScript, подробно описан здесь:

Вот две функции из фреймворка xa.js, которые я прикрепляю к utils = {} 'контейнер'. Это должно помочь вам правильно определять массивы.

var utils = {};

/**
 * utils.isArray
 *
 * Best guess if object is an array.
 */
utils.isArray = function(obj) {
     // do an instanceof check first
     if (obj instanceof Array) {
         return true;
     }
     // then check for obvious falses
     if (typeof obj !== 'object') {
         return false;
     }
     if (utils.type(obj) === 'array') {
         return true;
     }
     return false;
 };

/**
 * utils.type
 *
 * Attempt to ascertain actual object type.
 */
utils.type = function(obj) {
    if (obj === null || typeof obj === 'undefined') {
        return String (obj);
    }
    return Object.prototype.toString.call(obj)
        .replace(/\[object ([a-zA-Z]+)\]/, '$1').toLowerCase();
};

Если вы хотите проверить, находится ли объект в массиве, я бы также включил этот код:

/**
 * Adding hasOwnProperty method if needed.
 */
if (typeof Object.prototype.hasOwnProperty !== 'function') {
    Object.prototype.hasOwnProperty = function (prop) {
        var type = utils.type(this);
        type = type.charAt(0).toUpperCase() + type.substr(1);
        return this[prop] !== undefined
            && this[prop] !== window[type].prototype[prop];
    };
}

И, наконец, эта функция in_array:

function in_array (needle, haystack, strict) {
    var key;

    if (strict) {
        for (key in haystack) {
            if (!haystack.hasOwnProperty[key]) continue;

            if (haystack[key] === needle) {
                return true;
            }
        }
    } else {
        for (key in haystack) {
            if (!haystack.hasOwnProperty[key]) continue;

            if (haystack[key] == needle) {
                return true;
            }
        }
    }

    return false;
}
Другие вопросы по тегам