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

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 ()

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

jbabey
источник
+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);
PirateApp
источник
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
var indices = [];
var IDs = [0, 1, 2, 3, 4];

for(var i = 0, len = array.length; i < len; i++) {
    for(var j = 0; j < IDs.length; j++) {
        if(array[i].id == ID) indices.push(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
enapupe
источник
3

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

let idToFind = 3;
let index = someArray.map(obj => obj.id).indexOf(idToFind);
JoeTidee
источник
3

Новый способ использования ES6

let picked_element = array.filter(element => element.id === 0);
Silve2611
источник
picked_elementэто массив в этом случае ...
Еретик Обезьяна
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);

PulpDood
источник
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
Tejs
источник
2

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

matchingIndices.push(j);

в

matchingIndices.push(NumberInt(j+1));
user2584621
источник
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, [])

trungk18
источник
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'}
patotoma
источник
Вы можете прочитать Как предложить персональные библиотеки с открытым исходным кодом? прежде чем опубликовать это везде.
Мартин Питерс
@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 );
Даниэль Лефевр
источник