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


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.