Як отримати індекс об’єкта за його властивістю в JavaScript?


241

Наприклад, у мене є:

var Data = [
  { id_list: 1, name: 'Nick', token: '312312' },
  { id_list: 2, name: 'John', token: '123123' },
]

Потім, я хочу сортувати / повернути цей об'єкт name, наприклад. І тоді я хочу отримати щось подібне:

var Data = [
  { id_list: 2, name: 'John', token: '123123' },
  { id_list: 1, name: 'Nick', token: '312312' },
]

А тепер я хочу знати індекс об’єкта з властивістю, name='John'щоб отримати значення маркера властивості.

Як вирішити проблему?


Чому ви бажаєте спочатку сортувати список, перш ніж шукати об’єкт?
JJJ

Об'єкти JavaScript є {Key:Value}, я виправив це для вас.
Ракета Hazmat


Якщо ви скануєте відповіді, виявиться, що є якийсь рідний Dataоб’єкт. Це просто велике ім'я змінної, яке суперечить умові. Якщо когось це турбує, я внесу зміни до питання та відповіді, щоб виправити це іменування.
Roy Prins

Відповіді:


170

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

function findWithAttr(array, attr, value) {
    for(var i = 0; i < array.length; i += 1) {
        if(array[i][attr] === value) {
            return i;
        }
    }
    return -1;
}

var Data = [
    {id_list: 2, name: 'John', token: '123123'},
    {id_list: 1, name: 'Nick', token: '312312'}
];

З цим ви зможете не тільки знайти, який містить "Джон", але і той, що містить маркер "312312":

findWithAttr(Data, 'name', 'John'); // returns 0
findWithAttr(Data, 'token', '312312'); // returns 1
findWithAttr(Data, 'id_list', '10'); // returns -1

EDIT: оновлена ​​функція для повернення -1, коли її не знайдено, тому вона відповідає тій же конструкції, що і Array.prototype.indexOf ()


1
Додано відповідь, що розширює цю функцію на пошук глибшого, ніж один рівень атрибутів. Дякую за це Кріс - дуже допомогло: D
Ед Ши

3
Тим, хто матиме проблеми з цим рішенням, пам’ятайте, що знак === рівність перевірятиме не лише значення, але й тип даних. тому порівняння дати, чисел та порожніх значень може повернути помилкові, якщо фактичне значення є рядком. Ви можете використовувати знак == рівності, якщо тип даних для вас не суттєвий.
David Addoteye

НЕ РОБИТИ ЦЕ. Дізнайтеся, як використовувати функції вищого порядку, це старий спосіб робити.
Led

327

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

Ваш приклад:

var Data = [
    {id_list:1, name:'Nick',token:'312312'},
    {id_list:2,name:'John',token:'123123'}
]

Ви можете зробити:

var index = Data.map(function(e) { return e.name; }).indexOf('Nick');

Array.prototype.mapнедоступний на IE7 або IE8. Сумісність ES5

І ось це з синтаксисом ES6 та стрілками, що ще простіше:

const index = Data.map(e => e.name).indexOf('Nick');

4
Я маю повторити весь масив, незважаючи ні на що. Перший метод цього не потрібно.
Німецький Аттанасьо

1
Просте, але
враховуйте

1
Дійсно чудова відповідь допомогла мені у цій проблемі!
NiallMitch14

1
Це рішення прекрасне. Нехай ви доживете до тисячі років, сер.
заглиблення

3
Якщо зробити карту, а потім indexOf у версії ES6, це означає, що вони двічі будуть перебирати масив. Замість цього слід використовувати метод findIndex , показаний в моїй обороні
silverlight513

209

Якщо ви добре використовуєте ES6. Масиви тепер мають функцію findIndex . Що означає, що ви можете зробити щось подібне:

const index = Data.findIndex(item => item.name === 'John');

6
руками вниз найбільш пряме і елегантне рішення, я б зробив невелике не те, щоб findIndex був жадібним - в моєму випадку це було ідеально - але користувачі знають, що якщо у вас є об'єкти з подвійними значеннями (тобто два об'єкти з name: John), це повернеться лише перший матч
GrayedFox

Дивовижно !! Дякую.
moreirapontocom

@GrayedFox ти не мав на увазі НЕ жадібний? Жадібний означає більше результатів, а findIndex повертає лише перший матч.
Жос

1
Вибачте @Jos, але я думаю, ви не зовсім розумієте, що робить жадібний алгоритм пошуку . Жадібний алгоритм розглядає лише ту інформацію, яку він має під рукою, яка обчислювально ефективна для, скажімо, пошуку елемента в списку, коли вам потрібен лише один результат. Метод findIndex є жадібним, оскільки він повертає лише перший збіг. Якщо б не було жадібним, воно б продовжувало пошуки. Ви думаєте про термін "жадібний", як його вживають у щоденній англійській мові, але в контексті програмування (тобто ТАК) це протилежне тому, що ви думаєте;)
GrayedFox

Дякуємо за пояснення @GrayedFox. Моє посилання на жадібне було насправді від програмування, але пов’язане з регулярними виразами, де жадібніші беруть більше символів, ніж не жадібні! :-)
Жос

28
var index = Data.findIndex(item => item.name == "John")

Яка спрощена версія:

var index = Data.findIndex(function(item){ return item.name == "John"})

З mozilla.org:

Метод findIndex () повертає індекс першого елемента в масиві, який задовольняє наданій функції тестування. Інакше -1 повертається.


2
Використовуйте "===" для порівняння рядків - це єдине, чого не вистачає у цій відповіді.
Бруно Таварес

@BrunoTavares, який сценарій, на вашу думку, був би іншим у цьому випадку?
edank

1
@edank Якщо ОП перевірять tokenполе замість nameполя, то можуть виникнути проблеми, як Data[0].token == 312312оцінює true, але Data[0].token === 321321оцінює false.
kem

@edank погляньте на цю відповідь stackoverflow.com/questions/359494/…
Бруно Таварес

23

Якщо у вас є проблеми з IE, ви можете використовувати функцію map (), яка підтримується з 9.0:

var index = Data.map(item => item.name).indexOf("Nick");

1
Відповідь нижче надає більше інформації та була розміщена першою.
Олександр

Це, безумовно, найкраще рішення для цього.
Беболічний

4

Єдиний відомий мені спосіб - це прокручування всього масиву:

var index=-1;
for(var i=0;i<Data.length;i++)
  if(Data[i].name==="John"){index=i;break;}

або нечутливий до регістру:

var index=-1;
for(var i=0;i<Data.length;i++)
  if(Data[i].name.toLowerCase()==="john"){index=i;break;}

В результаті змінний індекс містить індекс об'єкта або -1, якщо його не знайти.


2

прототипний спосіб

(function(){
  if (!Array.prototype.indexOfPropertyValue){
    Array.prototype.indexOfPropertyValue = function(prop,value){
      for (var index = 0; index < this.length; index++){
        if (this[index][prop]){
          if (this[index][prop] == value){
            return index;
          }
        }
      }
      return -1;
    }
  }
 })();
 // usage:
 var Data = [
 {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}];
 Data.indexOfPropertyValue('name','John'); // returns 1 (index of array);
 Data.indexOfPropertyValue('name','Invalid name') // returns -1 (no result);
 var indexOfArray = Data.indexOfPropertyValue('name','John');
 Data[indexOfArray] // returns desired object.

Мені довелося це трохи змінити, щоб працювати в моїй ситуації - я шукав індекс властивості зі значенням null, а п'ятий рядок змусив це пропустити цей індекс.
Девід Брунов

1

Ви можете використовувати Array.sort, використовуючи власну функцію як параметр для визначення свого сортування.

У вашому прикладі це дасть:

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
]

Data.sort(function(a, b){
    return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
});

alert("First name is : " + Data[0].name); // alerts 'John'
alert("Second name is : " + Data[1].name); // alerts 'Nick'

Функція сортування повинна повертати або -1, якщо a має бути перед b, 1, якщо a має прийти після b і 0, якщо обидва рівні. Ви повинні визначити правильну логіку у своїй функції сортування для сортування масиву.

Редагувати: Пропущено останню частину запитання, де ви хочете знати індекс. Вам доведеться провести цикл через масив, щоб знайти, як сказали інші.


1

Просто перейдіть через свій масив і знайдіть позицію:

var i = 0;
for(var item in Data) {
    if(Data[item].name == 'John')
        break;
    i++;
}
alert(i);

Це має бути if(Data[item].name == 'John'), я це зафіксував.
Ракета Hazmat

У цього є один момент. Якщо змінна "не знайдено", я містять позитивний індекс. І для тестування "не знайдено" потрібно порівняти, якщо i === Data.length
Ендрю Д.

Другий момент - це якщо масив містить власні властивості неіндексатора. Наприклад: var Data [1,2,3,4,5]; Дані ["тест"] = 7897; Порівняйте вихід: для (var i in Data) попередження (Data [i]); і для (var i = 0; i <Data.length; i ++) попередження (дані [i]);
Ендрю Д.


1

Чому б вам не скористатися невеликою обробкою?

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

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
    ]
var searchArr = []
Data.forEach(function(one){
  searchArr[one.name]=one;
})
console.log(searchArr['Nick'])

http://jsbin.com/xibala/1/edit

живий приклад.


Це означає, що спочатку прокручуємо все, а потім змінюємо об'єкти (якщо ви їх не клонуєте, але це ще більше накладних витрат). А після цього ви знову петля (хоча б частково).
Жос

1

Розширена відповідь Кріса Пікетта, оскільки в моєму випадку мені потрібно було шукати глибше одного рівня атрибутів:

function findWithAttr(array, attr, value) {
  if (attr.indexOf('.') >= 0) {
    var split = attr.split('.');
    var attr1 = split[0];
    var attr2 = split[1];
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr1][attr2] === value) {
        return i;
      }
    }
  } else {
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr] === value) {
        return i;
      }
    }
  };
};

Ви можете передати 'attr1.attr2' у функцію


1

Як що до цього ? :

Data.indexOf(_.find(Data, function(element) {
  return element.name === 'John';
}));

Припустимо, що ви використовуєте lodash або підкреслення.


1
var fields = {
teste:
{
    Acess:
    {
        Edit: true,
      View: false

      }
 },
teste1:
{
    Acess:
    {
        Edit: false,
      View: false

      }
  }
};

console.log(find(fields,'teste'));
function find(fields,field){
    for(key in fields){
        if(key == field){
        return true;
    }
}
return false;
}

Якщо у вас є один Об'єкт з множими об'єктами всередині, і якщо ви хочете знати, чи є якийсь об'єкт включений до головного об'єкта, просто поставте find (MasterObject, 'Object to Search'), ця функція поверне відповідь, якщо вона існує чи ні (TRUE або FALSE ), Я сподіваюсь допомогти у цьому, можна побачити приклад на JSFiddle .


Додайте пояснення з відповіддю, як ця відповідь допоможе ОП у вирішенні поточного питання
ρяσѕρєя K

@ ρyaσѕρєяK, Дякую за вашу пропозицію, я вже додаю пояснення :)
Педро Монтейро Арантес

1
let indexOf = -1;
let theProperty = "value"
let searchFor = "something";

theArray.every(function (element, index) {

    if (element[theProperty] === searchFor) {
        indexOf = index;
        return false;
    }
    return true;
});

1
collection.findIndex(item => item.value === 'smth') !== -1

Хоча цей фрагмент коду може вирішити питання, у тому числі пояснення дійсно допомагає покращити якість вашої публікації. Пам'ятайте, що ви відповідаєте на запитання читачів у майбутньому, і ці люди можуть не знати причини вашої пропозиції щодо коду. Будь ласка, намагайтеся не переповнювати свій код пояснювальними коментарями, оскільки це зменшує читабельність і коду, і пояснень!
Goodbye StackExchange

1

Якщо ви хочете отримати значення маркера властивості, ви також можете спробувати це

    let data=[
      { id_list: 1, name: 'Nick', token: '312312' },
      { id_list: 2, name: 'John', token: '123123' },
    ]

    let resultingToken =  data[_.findKey(data,['name','John'])].token

де _.findKey - функція лодаша


0

Як варіант відповіді німецького Аттанасіо Руїса, ви можете усунути 2-й цикл, використовуючи Array.reduce () замість Array.map ();

var Data = [
    { name: 'hypno7oad' }
]
var indexOfTarget = Data.reduce(function (indexOfTarget, element, currentIndex) {
    return (element.name === 'hypno7oad') ? currentIndex : indexOfTarget;
}, -1);

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