В массиве объектов самый быстрый способ найти индекс объекта, атрибуты которого соответствуют поисковому запросу.


135

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

array[i].id = some number;
array[i].name = some name;

То, что я хочу сделать, - это найти ИНДЕКСЫ объектов, где id равен, например, одному из 0,1,2,3 или 4. Я полагаю, я мог бы просто сделать что-то вроде:

var indexes = [];
for(i=0; i<array.length; i++) {
  (array[i].id === 0) ? { indexes[0] = i }
  (array[i].id === 1) ? { indexes[1] = i }
  (array[i].id === 2) ? { indexes[2] = i }
  (array[i].id === 3) ? { indexes[3] = i }
  (array[i].id === 4) ? { indexes[4] = i }
}

Хотя это будет работать, оно выглядит довольно дорогим и медленным (не говоря уже об уродливости), особенно если array.length может быть большим. Любые идеи о том, как украсить это немного? Я думал об использовании array.indexOf как-то, но я не вижу, как заставить синтаксис. это

array.indexOf(this.id === 0);

например, возвращает undefined, как это должно быть. Заранее спасибо!


1
Если у вас есть простой старый массив, все, что вы можете сделать, это итерировать Вот что такое массивы, набор объектов, упорядоченных по индексу массива.
— Дэйв Ньютон

2
Просто ознакомьтесь с этим сообщением сегодня, для всех опоздавших Array.prototype.findIndex()в ECMAScript 2015 появился новый метод массива . Принятый ответ был потрясающим.
— Конрад Ло

Я фанат синтаксиса ES6 (используйте полифилы, если требуется поддержка в старых браузерах). ES7 + ES8 будут в будущем
— Fr0zenFyr

Ответы:


391

Может быть, вы хотели бы использовать функции более высокого порядка, такие как «карта». Предполагая, что вам нужен поиск по атрибуту 'field':

var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);
var objectFound = array[elementPos];

9
Этот ответ великолепен, потому что на самом деле он отвечает на вопрос, предоставляя индекс :)
— противодействие

3
@ZeroAbsolute Ваша прикладная функция (переданная на карту) может вернуть хеш-строку, которая должна предоставить уникальный ключ для каждой возможной комбинации, заданной вашими критериями. Например: function hashf(el) { return String(el.id) + "_" + String(el.name); }. Это всего лишь подсказка: elementPos = array.map(hashf(x)).indexOf(hash({id:3, name:'Pablo'}));очевидно, хеш-функция, которую я предоставляю, недействительна для всех случаев, поскольку '_'может составлять часть ваших значений, но это лишь быстрый пример того, как можно найти различные методы хеширования.
— Пабло Франсиско Перес Идальго

1
Что это возвращает, если он не найден? Я предполагаю -1, просто любопытно. Я буду экспериментировать.
— Натан С. Треш

1
@ NathanC.Tresch Возвращает -1, потому что это indexOfвозвращаемое значение, когда он не может найти данное значение.
— Пабло Франсиско Перес Идальго

2
Привет всем, вместо того, чтобы использовать два метода, map, indexOfвы можете использовать только один, называемый findIndex....... Пример:[{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3}) OR [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)
— Умайр Ахмед

64

Самый простой и легкий способ найти индекс элемента в массиве.

Синтаксис ES5: [{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3})

Синтаксис ES6: [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)


4
Я считаю, что это самое элегантное решение. Для тех, кто беспокоится о обратной совместимости, вы можете найти полифилл findIndexна сайте developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
— mrogers

2
Я получаю предупреждение в своем инструменте ES6 lint, что obj.id == 3используемый здесь оператор может вызвать неожиданное преобразование типа, поэтому используйте obj.id === 3вместо этого оператор, который проверяет одинаковое значение и тип.
— спасибо

1
Этот ответ как минимум в 3,5 раза быстрее, чем принятый ответ выше. Использование var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);заняло 0,03500000002532033 миллисекунд. Использование [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)заняло 0,00999999747378752 миллисекунд.
— Овидио Рейна

1
ЭТОТ ОТВЕТ является наиболее ЭФФЕКТИВНЫМ, поскольку он не выполняет итерацию всего массива. Выбранный ответ отобразит полный массив, а затем findIndex, который должен выполнить итерацию по всему массиву один раз
— Karun

26

Новый метод Array .filter () будет хорошо работать для этого:

var filteredArray = array.filter(function (element) { 
    return element.id === 0;
});

JQuery также может сделать это с помощью .grep ()

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


+1, я всегда забываю о таких встроенных функциях на объектах.
— Tejs

59
Это не возвращает индекс.
— Адам Грант

Это не отвечает на этот конкретный вопрос, но мне очень помогает! Спасибо!
— rochasdv

Это не возвращает индекс.
— Рич

10

Если вы заботитесь о производительности, не идет с находкой или фильтром или карты или любые из рассмотренных выше методов

Вот пример, демонстрирующий самый быстрый метод. ЗДЕСЬ ссылка на актуальный тест

Блок настройки

var items = []

for(var i = 0; i < 1000; i++) {
    items.push({id: i + 1})
}

var find = 523

Самый быстрый метод

var index = -1
for(var i = 0; i < items.length; i++) {
    if(items[i].id === find) {
        index = i;
        break;
    }
}

Более медленные методы

items.findIndex(item => item.id === find)

МЕДЛЕННЫЙ метод

items.map(item => item.id).indexOf(find);

2
Спасибо за предоставленное сравнение! Что очень интересно, так это то, насколько сильно меняется производительность, в том числе какой метод меняется быстрее, в зависимости от того, какой браузер / движок JavaScript их использовал.
— Иэн Коллинз

1
Я думаю, что это должно быть отмечено как ответ. Это показывает самый быстрый и медленный путь.
— болеутоляющее

В вашем тесте блок 2 (с использованием findIndex) на самом деле быстрее для меня (на Microsoft Edge Chromium 83.0.474.0)
— rezadru

Блок 2 теперь работает быстрее и на Chrome
— Cody Mikol

8
array.forEach(function (elem, i) {  // iterate over all elements of array
    indexes[elem.id] = i;           // take the found id as index for the
});                                 // indexes array and assign i

Результатом является список поиска для идентификатора. с заданным идентификатором мы получаем индекс записи.



6

Поскольку нет ответа с использованием обычного массива find:

var one = {id: 1, name: 'one'};
var two = {id: 2, name:'two'}
var arr = [one, two] 

var found = arr.find((a) => a.id === 2)

found === two // true

arr.indexOf(found) // 1

3

Используя mapфункцию ES6 :

let idToFind = 3;
let index = someArray.map(obj => obj.id).indexOf(idToFind);


3

const index = array.findIndex(item => item.id === 'your-id');

Это должно получить индекс предмета в массиве с id === your-id

array = [ {id:1}, {id:2} ];

const index = array.findIndex(item => item.id === 2);

console.log(index);


2

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

function findElements(array, predicate)
{
    var matchingIndices = [];

    for(var j = 0; j < array.length; j++)
    {
        if(predicate(array[j]))
           matchingIndices.push(j);
    }

    return matchingIndices;
}

Тогда вы можете вызвать так:

var someArray = [
     { id: 1, text: "Hello" },
     { id: 2, text: "World" },
     { id: 3, text: "Sup" },
     { id: 4, text: "Dawg" }
  ];

var matchingIndices = findElements(someArray, function(item)
   {
        return item.id % 2 == 0;
   });

// Should have an array of [1, 3] as the indexes that matched

2

Приспосабливая ответ Tejs для mongoDB и Robomongo, я изменился

matchingIndices.push(j);

в

matchingIndices.push(NumberInt(j+1));

2

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

  1. Вернуть индекс первого вхождения.

const array = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 2 }];
const idYourAreLookingFor = 2;

//ES5 
//Output: 1
array.map(function (x) { return x.id; }).indexOf(idYourAreLookingFor);

//ES6 
//Output: 1
array.findIndex(obj => obj.id === idYourAreLookingFor);

  1. Чтобы вернуть индексный массив всех вхождений, используйте Reduce.

const array = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 2 }]
const idYourAreLookingFor = 2;

//ES5
//Output: [1, 4]
array.reduce(function (acc, obj, i) {
  if (obj.id === idYourAreLookingFor)
    acc.push(i);
  return acc;
}, []);

//ES6
//Output: [1, 4]
array.reduce((acc, obj, i) => (obj.id === idYourAreLookingFor) ? acc.concat(i) : acc, [])


0

Поскольку я пока не могу комментировать, я хочу показать решение, которое я использовал, основываясь на методе, опубликованном Умаиром Ахмедом, но когда вы хотите найти ключ вместо значения:

[{"a":true}, {"f":true}, {"g":false}]
.findIndex(function(element){return Object.keys(element)[0] == "g"});

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


0

Я создал крошечную утилиту под названием super-array, где вы можете получить доступ к элементам массива по уникальному идентификатору со сложностью O (1). Пример:

const SuperArray = require('super-array');

const myArray = new SuperArray([
  {id: 'ab1', name: 'John'},
  {id: 'ab2', name: 'Peter'},
]);

console.log(myArray.get('ab1')); // {id: 'ab1', name: 'John'}
console.log(myArray.get('ab2')); // {id: 'ab2', name: 'Peter'}

Вы можете прочитать Как предложить персональные библиотеки с открытым исходным кодом? прежде чем опубликовать это везде.
— Мартин Питерс

@MartijnPieters Я разместил его только на несколько уместных вопросов, и проект является бесплатным MIT, так в чем же дело? Может быть, вы могли бы быть немного более терпимым.
— Патотома

0
var test = [
  {id:1, test: 1},
  {id:2, test: 2},
  {id:2, test: 2}
];

var result = test.findIndex(findIndex, '2');

console.log(result);

function findIndex(object) {
  return object.id == this;
}

вернет индекс 1 (работает только в ES 2016)


0

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

 while(i<myArray.length && myArray[i].data.value!==value){
  i++; 
}
// i now hows the index value for the match. 
 console.log("Index ->",i );
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.