Метод 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.