Получить индекс объекта внутри массива, соответствующий условию

322

У меня есть такой массив:

[{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"},...]

Как я могу получить индекс объекта, который соответствует условию, без итерации по всему массиву?

Например, учитывая prop2=="yutu", я хочу получить индекс 1.

Я видел, .indexOf()но думаю, что он используется для простых массивов, как ["a1","a2",...]. Я также проверил, $.grep()но это возвращает объекты, а не индекс.

ампер
источник

Ответы:

733

Начиная с 2016 года, вы должны использовать Array.findIndex(стандарт ES2015 / ES6) для этого:

a = [
  {prop1:"abc",prop2:"qwe"},
  {prop1:"bnmb",prop2:"yutu"},
  {prop1:"zxvz",prop2:"qwrq"}];
    
index = a.findIndex(x => x.prop2 ==="yutu");

console.log(index);

Поддерживается в Google Chrome, Firefox и Edge. Для Internet Explorer на связанной странице есть полифилл.

Примечание о производительности

Вызовы функций дороги, поэтому с действительно большими массивами простой цикл будет работать намного лучше, чем findIndex:

let test = [];

for (let i = 0; i < 1e6; i++)
    test.push({prop: i});


let search = test.length - 1;
let count = 100;

console.time('findIndex/predefined function');
    let fn = obj => obj.prop === search;

    for (let i = 0; i < count; i++)
        test.findIndex(fn);
console.timeEnd('findIndex/predefined function');


console.time('findIndex/dynamic function');
    for (let i = 0; i < count; i++)
        test.findIndex(obj => obj.prop === search);
console.timeEnd('findIndex/dynamic function');


console.time('loop');
    for (let i = 0; i < count; i++) {
        for (let index = 0; index < test.length; index++) {
            if (test[index].prop === search) {
                break;
            }
        }
    }
console.timeEnd('loop');

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

георг
источник
3
Я не вижу необходимости во временном массиве здесь. Просто используйте тот факт, что функция итератора закрывает контекст, и используйте переменную. Кроме того, не-jQuery-версия не работает (предположим, она найдена в индексе 0?). Оба решения выполняют больше итераций, чем требуется, что не идеально, если массив большой (хотя вероятность того, что он будет настолько большим, что человек заметит, низка, если поиск не происходит много ).
TJ Crowder
@ thg435: Все еще думаю, что это что-то вроде машины Рубе Голдберга, в которой простой рычаг справился бы. :-) Но эй, это работает!
TJ Crowder
4
Можете ли вы объяснить, как x => x.prop2=="yutu"работает с findIndex ()?
Абхай Пай
5
@AbhayPai: это то же самое, что иfunction(x) { return x.prop2=="yutu" }
georg
6
Мне нравится предложение использовать полифилл. Однако написанный код все еще не работает в IE11 даже с использованием функции polyfill из-за использования функции стрелки / лямбды. Переписан как index = a.findIndex(function (x) { return x.prop2 == "yutu" })исправленный вопрос, так что с помощью кода polyfill findIndex работал в IE11
Рик Глос,
26

Как я могу получить индекс объекта, который соответствует условию (без итерации по массиву)?

Вы не можете, что-то должно пройти через массив (хотя бы один раз).

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

var index;
yourArray.some(function(entry, i) {
    if (entry.prop2 == "yutu") {
        index = i;
        return true;
    }
});

При этом используется новая Array#someфункция (ish) , которая перебирает записи в массиве до тех пор, пока функция, которую вы ей дадите, не вернет true. Функция, которую я дал, сохраняет индекс соответствующей записи, а затем возвращает, trueчтобы остановить итерацию.

Или, конечно, просто используйте forцикл. Различные варианты итерации описаны в этом другом ответе .

Но если вы всегда собираетесь использовать одно и то же свойство для этого поиска, и если значения свойств уникальны, вы можете выполнить цикл только один раз и создать объект для их отображения:

var prop2map = {};
yourArray.forEach(function(entry) {
    prop2map[entry.prop2] = entry;
});

(Или, опять же, вы можете использовать forцикл или любой другой вариант .)

Затем, если вам нужно найти запись с помощью prop2 = "yutu", вы можете сделать это:

var entry = prop2map["yutu"];

Я называю это «перекрестной индексацией» массива. Естественно, если вы удаляете или добавляете записи (или изменяете их prop2значения), вам также необходимо обновить объект сопоставления.

TJ Crowder
источник
Спасибо за объяснение! Решение с использованием jQuery thg435сделало то, что я хотел ...
amp
21

Как сказал TJ Crowder, каждый раз будет иметь какую-то скрытую итерацию, с lodash это становится:

var index = _.findIndex(array, {prop2: 'yutu'})
aliak
источник
1
в то время как вы можете просто просмотреть различные способы получения индекса, найдите Index - лучшее решение, даже принятое в ES6 в качестве методов работы с массивами
Kelly Milligan,
13
var CarId = 23;

//x.VehicleId property to match in the object array
var carIndex = CarsList.map(function (x) { return x.VehicleId; }).indexOf(CarId);

И для базовых номеров массивов вы также можете сделать это:

var numberList = [100,200,300,400,500];
var index = numberList.indexOf(200); // 1

Вы получите -1, если не сможете найти значение в массиве.

Дэвид Кастро
источник
11
var index;
yourArray.some(function (elem, i) {
    return elem.prop2 === 'yutu' ? (index = i, true) : false;
});

Итерировать по всем элементам массива. Возвращает индекс и true или false, если условие не совпадает.

Важным является явное возвращаемое значение true (или значение, логический результат которого равен true). Одного присваивания недостаточно из-за возможного индекса с 0 (Boolean (0) === false), который не приведет к ошибке, но отключит разрыв итерации.

редактировать

Еще более короткая версия вышеупомянутого:

yourArray.some(function (elem, i) {
    return elem.prop2 === 'yutu' && ~(index = i);
});
Нина Шольц
источник
Что делает персонаж в вашем втором фрагменте?
серкан
@serkan, это побитовый| оператор NOT , это короткая версия получения из индекса (с -1) истинного / ложного результата, если индекс существует.
Нина Шольц
спасибо Нина, без символа ~ код работает как есть, не так ли?
серкан
@serkan, твой вопрос не понятен, но без ~него так не работает.
Нина Шольц
1
о, !!(index = 0)и !!~(index = 0)разница действительно. Спасибо!
серкан
4

Вы можете использовать Array.prototype.some () следующим образом (как указано в других ответах):

https://jsfiddle.net/h1d69exj/2/

function findIndexInData(data, property, value) {
    var result = -1;
    data.some(function (item, i) {
        if (item[property] === value) {
            result = i;
            return true;
        }
    });
    return result;
}
var data = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]



alert(findIndexInData(data, 'prop2', "yutu")); // shows index of 1
GibboK
источник
4

Я видел много решений в выше.

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

Я собираюсь объяснить свой ответ с использованием данных студентов.

  • Шаг 1 : создайте объект массива для студентов (необязательно, вы можете создать свой собственный объект массива).
    var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

  • шаг 2 : создайте переменную для поиска текста
    var studentNameToSearch = "Divya";

  • Шаг 3 : Создайте переменную для хранения сопоставленного индекса (здесь мы используем функцию map для итерации).
    var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

var studentNameToSearch = "Divya";

var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

console.log(matchedIndex);

alert("Your search name index in array is:"+matchedIndex)

Рамбабу Боммисетти
источник
3
function findIndexByKeyValue(_array, key, value) {
    for (var i = 0; i < _array.length; i++) { 
        if (_array[i][key] == value) {
            return i;
        }
    }
    return -1;
}
var a = [
    {prop1:"abc",prop2:"qwe"},
    {prop1:"bnmb",prop2:"yutu"},
    {prop1:"zxvz",prop2:"qwrq"}];
var index = findIndexByKeyValue(a, 'prop2', 'yutu');
console.log(index);
пранабеш чанд
источник
1

Почему вы не хотите повторять точно? Новый Array.prototype.forEach отлично подходит для этой цели!

Вы можете использовать Бинарное дерево поиска, чтобы найти с помощью одного вызова метода, если хотите. Это аккуратная реализация BTree и Красно-черного дерева поиска в JS - https://github.com/vadimg/js_bintrees - но я не уверен, сможете ли вы одновременно найти индекс.

Rishabh
источник
1

Один шаг с использованием Array.reduce () - без jQuery

var items = [{id: 331}, {id: 220}, {id: 872}];

var searchIndexForId = 220;
var index = items.reduce(function(searchIndex, item, index){
  if(item.id === searchIndexForId) { 
    console.log('found!');
    searchIndex = index;
  }
  return searchIndex;
}, null);

вернется, nullесли индекс не был найден.

SagiSergeNadir
источник
0
var list =  [
                {prop1:"abc",prop2:"qwe"},
                {prop1:"bnmb",prop2:"yutu"},
                {prop1:"zxvz",prop2:"qwrq"}
            ];

var findProp = p => {
    var index = -1;
    $.each(list, (i, o) => {
        if(o.prop2 == p) {
            index = i;
            return false; // break
        }
    });
    return index; // -1 == not found, else == index
}
Рубен Моралес Феликс
источник
0

Георг уже упоминал, что ES6 имеет Array.findIndex для этого. И некоторые другие ответы обходятся для ES5 с использованием метода Array.some.

Еще один элегантный подход может быть

var index;
for(index = yourArray.length; index-- > 0 && yourArray[index].prop2 !== "yutu";);

В то же время я хотел бы подчеркнуть, что Array.some может быть реализован с помощью двоичного или другого эффективного метода поиска. Таким образом, он может работать лучше для цикла в некоторых браузерах.

Санджа
источник
0

Попробуйте этот код

var x = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]
let index = x.findIndex(x => x.prop1 === 'zxvz')
Трилок Сингх
источник