У масиві об'єктів найшвидший спосіб знайти індекс об'єкта, атрибути якого відповідають пошуку


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

наприклад, повертається невизначено, як це, мабуть, повинно. Спасибі заздалегідь!


1
Якщо у вас є звичайний старий масив, все, що ви можете зробити - це ітерація. Ось що таке масиви, купа об’єктів, упорядкованих індексом масиву.
Дейв Ньютон

2
Просто натрапте на цю посаду сьогодні, для всіх тих, хто пішов, існує новий метод масиву Array.prototype.findIndex()в ECMAScript 2015. Прийнята відповідь була приголомшливою.
Конрад Ло

Я прихильник синтаксису ES6 (використовуйте polyfills, якщо потрібна підтримка застарілих браузерів). 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, просто цікаво. Я буду експериментувати.
Nathan C. Tresch

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 для попередження я отримую попередження про те, що obj.id == 3тут використаний оператор може викликати несподіване перетворення типу, тому використовуйте obj.id === 3замість цього оператор, який перевіряє рівне значення та тип.
thclark

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
ЦЕ ВІДПОВІДЬ є найбільш ефективним, оскільки не повторює весь масив. Вибрана відповідь відобразить повний масив, а потім знайдеIndex, який одноразово повторюється через весь масив
Карун

26

Новий метод Array .filter () добре працює для цього:

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

jQuery також може це зробити за допомогою .grep ()

редагувати: варто відзначити, що обидві ці функції просто ітерують під капотом, між ними не буде помітної різниці в продуктивності та прокатки власної функції фільтра, але чому знову винаходити колесо.


+1, я завжди забуваю про вбудовані такі функції на об'єктах.
Тейс

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 тепер швидший і на хромі
Коді Мікол

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

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

Адаптувавши відповідь Тейса для mongoDB та Robomongo, я змінив

matchingIndices.push(j);

до

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

2

Використання функції ES6 map:

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

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. Щоб повернути індексний масив усіх подій, використовуючи зменшити.

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

Оскільки я поки не можу коментувати, я хочу показати рішення, яке я використав на основі опублікованого методом Umair Ahmed, але коли ви хочете шукати ключ замість значення:

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

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


0

Я створив крихітну утиліту під назвою super-масив, де ви можете отримати доступ до елементів масиву за допомогою унікального ідентифікатора зі складністю 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'}

Ви можете прочитати Як пропонувати персональні бібліотеки з відкритим кодом? перш ніж публікувати це скрізь.
Martijn Pieters

@MartijnPieters Я опублікував це лише на кілька відповідних питань, і проект MIT безкоштовний, тож у чому справа? Можливо, ви могли б трохи терпіти.
patotoma

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.