Метод indexOf в массиве объектов?


514

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

Представьте себе этот сценарий:

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

Теперь я хотел бы иметь indexOfобъект, helloсвойство 'stevie'которого в данном примере будет 1.

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


1
Вы хотите объединить два объекта helloи qaz?
Армин

Нет, я не Я хочу иметь список объектов в массиве.
Антонио Лагуна

Ах хорошо! Вы хотите знать позицию всего объекта в массиве, который имеет определенное свойство.
Армин

10
Я нашел очень простую функцию для решения этой точной проблемы с таким SO-ответом: var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor); var objectFound = array[elementPos]; [ссылка] ( stackoverflow.com/a/16100446/1937255 )
Рик

ES6 Array.indexOf лучше принятого ответа (если ES6 работает для вас) - см. Полный пример ниже
yar1

Ответы:


1045

Я думаю, что вы можете решить это в одну строку, используя функцию карты :

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

58
Это должно быть честно принятым ответом. Большинство браузеров в настоящее время поддерживаютArray.prototype.map()
AlbertEngelB

10
Это не поддерживается IE8, но, если это не проблема, это лучшее решение.
Антонио Лагуна

57
Гм ... разве не стоит отметить, что Array.prototype.map () создает целый новый массив, содержащий сопоставленные элементы? Итак, если у вас есть массив с 1000 элементами, вы сначала создали другой массив с 1000 элементами, а затем выполните поиск? Думаю, было бы целесообразно увидеть производительность этого метода по сравнению с простым циклом for. Особенно, когда вы работаете на мобильной платформе с ограниченными ресурсами.
Дуг

7
@Doug Хотя ваша точка зрения на производительность действительно верна, кто в здравом уме заменит одну строку кода на семь для приложения, которое почти по определению связано с вводом-выводом / сетью, пока они не будут профилированы для выявления узких мест?
Джаред Смит

6
Технически минимизированный js-файл также состоит из одной строки; D
moreKing

371

Array.prototype.findIndex поддерживается во всех браузерах, кроме IE (без края). Но предоставленный polyfill хорош.

var indexOfStevie = myArray.findIndex(i => i.hello === "stevie");

Решение с картой в порядке. Но вы перебираете весь массив при каждом поиске. Это только наихудший случай для findIndex, который останавливает итерацию, как только найдено совпадение.


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

var searchTerm = "stevie",
    index = -1;
for(var i = 0, len = myArray.length; i < len; i++) {
    if (myArray[i].hello === searchTerm) {
        index = i;
        break;
    }
}

или как функция:

function arrayObjectIndexOf(myArray, searchTerm, property) {
    for(var i = 0, len = myArray.length; i < len; i++) {
        if (myArray[i][property] === searchTerm) return i;
    }
    return -1;
}
arrayObjectIndexOf(arr, "stevie", "hello"); // 1

Просто некоторые заметки:

  1. Не используйте для ... в циклах на массивах
  2. Обязательно вырвитесь из петли или выйдите из функции, как только вы нашли свою «иглу»
  3. Будьте осторожны с объектным равенством

Например,

var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found

у функции неправильное сравнение в searchterm, как и должно быть searchTerm :)
Антонио Лагуна

было несколько случаев
Джо

3
@SteveBennett - версия с оптимизированной производительностью; длина массива должна быть определена только один раз (когда инициализируются переменные для цикла for). В вашем случае длина проверяется каждую итерацию заново. См. Также stackoverflow.com/questions/5349425/… и stackoverflow.com/questions/8452317/… Однако, если производительность не высока prio, это на самом деле не имеет значения.
грабитель

1
Хороший ответ, но я провел некоторое тестирование производительности (см. Jsperf.com/find-index-of-object-in-array-by-contents ) и обнаружил, что упомянутый здесь ответ на основе функции, кажется, является вторым наиболее эффективным ответом. Единственная вещь, которая более производительна, в итоге ставит его в прототип, а не просто в функцию, как упоминалось в моем ответе .
Uniphonic

123

В ES2015 это довольно просто:

myArray.map(x => x.hello).indexOf('stevie')

или, возможно, с лучшей производительностью для больших массивов:

myArray.findIndex(x => x.hello === 'stevie')

2
Хороший подход к использованию ES6
kag

Я был удивлен, что ни один из этих методов не является столь же эффективным, как прототип цикла for, как упоминалось в моем ответе. Хотя поддержка браузера метода findIndex немного скудна, кажется, что он будет делать то же самое, но все равно в итоге будет менее производительным? Смотрите ссылку в моем ответе для оценки.
Uniphonic

Полезно знать, если производительность важна для поставленной задачи. Это очень редко, по моему опыту, но мммм.
Стив Беннетт

24
var idx = myArray.reduce( function( cur, val, index ){

    if( val.hello === "stevie" && cur === -1 ) {
        return index;
    }
    return cur;

}, -1 );

17

Мне нравится ответ Пабло, но Array # indexOf и Array # map не работают во всех браузерах. Underscore будет использовать нативный код, если он доступен, но также имеет и запасные варианты. Кроме того, у него есть метод «отрывки» для того, чтобы делать именно то, что делает метод анонимной карты Пабло.

var idx = _.chain(myArray).pluck("hello").indexOf("Stevie").value();

1
Array.prototype.map () поддерживается для IE9 +, и вы можете использовать Polyfill для IE8, 7, 6: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Иоганн Эчаваррия,

1
Вы можете использовать полифилл. , , или вы можете просто использовать подчеркивание или lodash, которые в основном являются полифиллами, к которым прикреплена целая куча других вкусностей . Что за возражение с подчеркиванием? Размер?
tandrewnichols

Мне очень нравится Underscore, ваш ответ тоже умный, но IMHO Пабло ответ самый чистый.
Иоганн Эчаваррия

Ух ты, я никогда не думал использовать цепочку таким образом. Мне действительно нравится, как это делает поиск беглым.
Дилан Пирс

chainздесь лишнее. _.pluck(myArray, 'hello').indexOf('stevie')
Стив Беннетт

14

Или прототип это:

Array.prototype.indexOfObject = function arrayObjectIndexOf(property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArr.indexOfObject("name", "stevie");

9
Очень удобно! Хотя я бы выбрал prototype.indexOfObject, чтобы не мешать существующему методу Array.indexOf. Array.prototype.indexOfObject = function(property, value) { for (var i = 0, len = this.length; i < len; i++) { if (this[i][property] === value) return i; } return -1; };
Адам

1
Я бы обернул его в самоисполняющееся замыкание со старым, сохраненным заранее, с первой строкой функции замены, являющейся чем-то вроде строки if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);. Это потому, что это несколько типов, которые являются неизменными. Я также добавил бы третий аргумент, чтобы включить откат к нативному методу, если это необходимо.
Исия Медоуз

8

краткое

myArray.indexOf('stevie','hello')

Случаи использования :

  /*****NORMAL****/  
[2,4,5].indexOf(4) ;//OUTPUT 1
 /****COMPLEX*****/
 [{slm:2},{slm:4},{slm:5}].indexOf(4,'slm');//OUTPUT 1
 //OR
 [{slm:2},{slm:4},{slm:5}].indexOf(4,function(e,i){
   return e.slm;
});//OUTPUT 1
/***MORE Complex**/
[{slm:{salat:2}},{slm:{salat:4}},{slm:{salat:5}}].indexOf(4,function(e,i){
   return e.slm.salat;
});//OUTPUT 1

API:

    Array.prototype.indexOfOld=Array.prototype.indexOf

    Array.prototype.indexOf=function(e,fn){
      if(!fn){return this.indexOfOld(e)}
      else{ 
       if(typeof fn ==='string'){var att=fn;fn=function(e){return e[att];}}
        return this.map(fn).indexOfOld(e);
      }
    };

6

Я провел тестирование производительности различных ответов, которые каждый может запустить самостоятельно:

https://jsperf.com/find-index-of-object-in-array-by-contents

Исходя из моих начальных тестов в Chrome, следующий метод (с использованием цикла for, настроенного внутри прототипа) является самым быстрым:

Array.prototype.indexOfObject = function (property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArray.indexOfObject("hello", "stevie");

Этот код является слегка измененной версией ответа Натана Заетты.

В тестах производительности я попробовал это сделать с целью, находящейся в середине (индекс 500) и в самом конце (индекс 999) массива объектов 1000, и даже если бы я поместил цель как самый последний элемент в массиве (т.е. что он должен пройти через каждый элемент массива, прежде чем он будет найден), он все равно оказывается самым быстрым.

Это решение также имеет преимущество в том, что оно является одним из самых кратких для многократного выполнения, поскольку нужно повторять только последнюю строку:

myArray.indexOfObject("hello", "stevie");

2
Я как раз собирался опубликовать ответ на этот вопрос, используя скрипку со своими собственными тестами, но благодаря вашему ответу мне больше не нужно. Я просто хочу подтвердить ваши тесты - я получил тот же результат, но с использованием whileцикла вместо forодного, и performance.now(). Я бы хотел, чтобы за этот ответ проголосовали больше, и чтобы я видел его раньше, он бы спас меня некоторое время ...
Инь Коньито


5

Хотя большинство других ответов здесь действительны. Иногда лучше просто сделать короткую простую функцию рядом с местом, где вы будете ее использовать.

// indexOf wrapper for the list of objects
function indexOfbyKey(obj_list, key, value) {
    for (index in obj_list) {
        if (obj_list[index][key] === value) return index;
    }
    return -1;
}
// Find the string in the list (default -1)
var test1 = indexOfbyKey(object_list, 'name', 'Stevie');
var test2 = indexOfbyKey(object_list, 'last_name', 'some other name');

Это зависит от того, что важно для вас. Это может сэкономить строки кода и быть очень умным, чтобы использовать однострочник, или поместить универсальное решение где-нибудь, которое покрывает различные крайние случаи. Но иногда лучше просто сказать: «вот так я это сделал», чем оставлять будущим разработчикам дополнительную работу по реинжинирингу. Особенно, если вы считаете себя «новичком», как в вашем вопросе.


4
array.filter(function(item, indx, arr){ return(item.hello === 'stevie'); })[0];

Вид [0].

Уместно использовать reduceкак в Antonio Lagunaответе.

Извиняюсь за краткость ...


4

Если ваш объект - это тот же объект, который вы используете в массиве, вы сможете получить индекс объекта так же, как если бы это была строка.

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var qazCLONE = { // new object instance and same structure
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [hello,qaz];

myArray.indexOf(qaz) // should return 1
myArray.indexOf(qazCLONE) // should return -1

Это ответ, который я искал, так как мне было неясно, соответствует ли IndexOf по значению или как. Теперь я знаю, что могу использовать IndexOf, чтобы найти свой объект, и не беспокоиться, если есть другие объекты с такими же свойствами.
MDave



3

Если вы заинтересованы только в поиске позиции, смотрите ответ @ Pablo .

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

Однако, если вы хотите найти элемент (то есть, если вы думаете о том, чтобы сделать что-то подобное myArray[pos]), есть более эффективный способ сделать это в одну строку , используя filter.

element = myArray.filter((e) => e.hello === 'stevie')[0];

Посмотреть результаты производительности (~ + 42% операций в секунду): http://jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3


2

Смотрите этот пример: http://jsfiddle.net/89C54/

for (i = 0; i < myArray.length; i++) {
    if (myArray[i].hello === 'stevie') {
        alert('position: ' + i);
        return;
    }
}

Начинает считать с нуля.


2

Я сделал общую функцию, чтобы проверить ниже код и работает для любого объекта

function indexOfExt(list, item) {
    var len = list.length;

    for (var i = 0; i < len; i++) {
        var keys = Object.keys(list[i]);
        var flg = true;
        for (var j = 0; j < keys.length; j++) {
            var value = list[i][keys[j]];
            if (item[keys[j]] !== value) {
                flg = false;
            }
        }
        if (flg == true) {
            return i;
        }
    }
    return -1;
}

var items = [{ "hello": 'world', "foo": 'bar' }];
var selectedItem = { "hello": 'world', "foo": 'bar' };
alert(items.indexOf(selectedItem));
alert(indexOfExt(items, selectedItem));

Первое оповещение вернет -1 (означает, что совпадение не найдено), а второе оповещение вернет 0 (значит совпадение найдено).


2

Использовать _.findIndexиз библиотеки underscore.js

Вот пример _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1


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

@ Стив Беннетт приятно использовать. Библиотеки теперь в Лодаше
Забуса

2

Используя findIndexметод ES6 , без lodash или каких-либо других библиотек, вы можете написать:

function deepIndexOf(arr, obj) {
  return arr.findIndex(function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

Это сравнит непосредственные свойства объекта, но не вернется в свойства.

Если ваша реализация еще не предоставляет findIndex(большинство не предоставляют), вы можете добавить легкий полифил, который поддерживает этот поиск:

function deepIndexOf(arr, obj) {
  function findIndex = Array.prototype.findIndex || function (pred) {
    for (let i = 0; i < this.length; ++i) {
      if (pred.call(this, this[i], i)) {
        return i;
      }
    }

    return -1;
  }

  return findIndex.call(arr, function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

(из моего ответа на этот обман )


2

Вы можете использовать встроенную и удобную функцию в Array.prototype.findIndex()основном:

Метод findIndex () возвращает индекс в массиве, если элемент в массиве удовлетворяет предоставленной функции тестирования. В противном случае -1 возвращается.

Просто обратите внимание, что он не поддерживается в Internet Explorer, Opera и Safari, но вы можете использовать Polyfill, предоставленный по ссылке ниже.

Больше информации:

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

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello, qaz);

var index = myArray.findIndex(function(element, index, array) {
  if (element.hello === 'stevie') {
    return true;
  }
});
alert('stevie is at index: ' + index);


2

Furthor @Monika Garg ответ , вы можете использовать findIndex()(есть полифилл для unsupprted браузеров).

Я видел, что люди отвергли этот ответ, и я надеюсь, что они сделали это из-за неправильного синтаксиса, потому что, на мой взгляд, это самый элегантный способ.

Метод findIndex () возвращает индекс в массиве, если элемент в массиве удовлетворяет предоставленной функции тестирования. В противном случае -1 возвращается.

Например:

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

var index = myArray.findIndex(function(element) {
  return element.hello == 'stevie';
});

alert(index);


Метод выглядит элегантно, но нет поддержки IE в соответствии с MDN? developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Яакко Карху

Попробуйте использовать polyfill (ссылка в ответе).
Мош Фё

1

Это способ найти индекс объекта в массиве

    var myArray = [{  hello: 'world',
        foo: 'bar'
    },{
        hello: 'stevie',
        foo: 'baz'
    }];



    for (i = 0; i < myArray.length; i++) {
        if (myArray[i].hello === 'stevie') {
            alert('position: ' + i);
            return;
        }
    }

0

Это работает без пользовательского кода

var arr, a, found;
arr = [{x: 1, y: 2}];
a = {x: 1, y: 2};
found = JSON.stringify(arr).indexOf(JSON.stringify(a)) > - 1;
// found === true

Примечание: это не дает фактический индекс, это только говорит, существует ли ваш объект в текущей структуре данных


1
Это недействительно, так как не позволяет получить какой-либо индекс
Антонио Лагуна

0

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

const someId = 2;
const array = [{id:1}, {id:2}, {id:3}];
const index = array.reduce((i, item, index) => item.id === someId ? index : i, -1);
alert('someId ' + someId + ' is at index ' + index);

Нет подчеркивания, нет, просто уменьшить.


0
var hello = {hello: "world",  foo: "bar"};
var qaz = {hello: "stevie", foo: "baz"};
var myArray = [];
myArray.push(hello,qaz);

function indexOfObject( arr, key, value   ) {
    var j = -1;
    var result = arr.some(function(obj, i) { 
        j++;
        return obj[key] == value;
    })

    if (!result) {
        return -1;
    } else {
        return j;
    };
}

alert(indexOfObject(myArray,"hello","world"));

Используйте Array некоторый метод.
user3235365

-1

Вы можете создать свой собственный прототип, чтобы сделать это:

что-то вроде:

Array.prototype.indexOfObject = function (object) {
    for (var i = 0; i < this.length; i++) {
        if (JSON.stringify(this[i]) === JSON.stringify(object))
            return i;
    }
}

2
Плохая практика, нарушающая инкапсуляцию: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/…
HMR

Это также будет нарушать рекурсивно определенные объекты.
Джозеф Коко

-2

Я предпочитаю использовать findIndex()метод:

 var index = myArray.findIndex('hello','stevie');

index даст вам номер индекса.


1
Ответ, орфография и код отступа и :) неверны?
Сачин Верма

1
findIndex отсутствует в любой стандартной реализации javascript. Предлагается (ecma 6) предложение для такого метода, но его подпись не такая. Пожалуйста, уточните, что вы имеете в виду (возможно, имя метода), дайте объявление метода findIndex или назовите библиотеку, которую вы используете.
Себастьян Ф.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.