Отримайте індекс об’єкта всередині масиву, який відповідає умові


321

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

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

Як я можу отримати індекс об'єкта, який відповідає умові, не повторюючи весь масив?

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

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

Відповіді:


732

З 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 навіть при полізасипці через використання функції стрілки / лямбда. index = a.findIndex(function (x) { return x.prop2 == "yutu" })Переписано як виправлене питання, щоб з кодом polyfill findIndex працював у IE11
Rick Glos

26

Як я можу змусити індекс об'єкта, який відповідає умові (без ітерації вздовж масиву)?

Ви не можете, щось має повторюватися через масив (принаймні один раз).

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

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

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

Або звичайно, просто використовуйте forпетлю. Ваші різні варіанти ітерації висвітлюються в цій іншій відповіді .

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

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

(Або, знову ж таки, ви можете використовувати forцикл або будь-який інший варіант .)

Тоді, якщо вам потрібно знайти запис prop2 = "yutu", ви можете зробити це:

var entry = prop2map["yutu"];

Я називаю цей масив "перехресним індексуванням". Природно, якщо ви видаляєте або додаєте записи (або змінюєте їх prop2значення), вам також потрібно оновити об’єкт відображення.


Дякую за пояснення! Рішення з jQuery, на яке вказував, thg435зробив те, що я хотів ...
amp

21

Що сказав TJ Crowder, у кожному разі буде мати якусь приховану ітерацію, з lodash це стає:

var index = _.findIndex(array, {prop2: 'yutu'})

1
в той час як ви можете просто переглядати різні способи отримання індексу, знайти Index - найкраще рішення, навіть прийняте в ES6, в методи рідного масиву
Келлі Мілліган

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 (або значення, булевий результат якого є true). Однозначного призначення недостатньо через можливий індекс з 0 (булева (0) === хибна), що не призведе до помилки, але вимкне перерву ітерації.

Редагувати

Ще коротша версія вищезазначеного:

yourArray.some(function (elem, i) {
    return elem.prop2 === 'yutu' && ~(index = i);
});

Що робить ~ персонаж у вашому другому фрагменті?
серкан

@serkan, це побітовое НЕ| оператор, це короткий варіант отримання з індексу (з -1) в truthy / falsy результаті, якщо індекс існує.
Ніна Шольц

дякую Ніна, Без ~ символу код працює як є, чи не так?
serkan

@serkan, ваше запитання не зрозуміло, але без ~нього не працює так.
Ніна Шольц

1
о, !!(index = 0)і !!~(index = 0)різниця дійсно. Дякую!
serkan

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

4

Я бачив багато рішень у вищесказаному.

Тут я використовую функцію map, щоб знайти індекс тексту пошуку в об’єкті масиву.

Я можу пояснити свою відповідь використанням даних студентів.

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

  • крок 2 : Створіть змінну для пошуку тексту
    var studentNameToSearch = "Divya";

  • крок 3 : Створіть змінну для зберігання відповідного індексу (тут ми використовуємо функцію карти для ітерації).
    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 та Red black дерева пошуку в JS - https://github.com/vadimg/js_bintrees - але я не впевнений, чи зможете ви одночасно знайти індекс.


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якщо індекс не був знайдений.


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')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.