Как получить индекс объекта по его свойству в 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}, я исправил это для вас.
Ракетный Хазмат


Если вы просматриваете ответы, кажется, что есть какой-то нативный Dataобъект. Это просто имя переменной с большой буквы, что противоречит соглашению. Если кого-то еще это беспокоит, я внесу изменения в вопрос и ответы, чтобы исправить это наименование.
Рой Принс

Ответы:


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

РЕДАКТИРОВАТЬ: Обновлена ​​функция для возврата -1, когда не найден, поэтому она следует той же конструкции, что и Array.prototype.indexOf ()


1
Добавлен ответ, расширяющий это, чтобы охватить поиск глубже, чем один уровень атрибута. Спасибо за это Крис - действительно помог: D
Эд Ши

3
Тем, у кого возникнут проблемы с этим решением, помните, что знак равенства === будет проверять не только значение, но и тип данных. поэтому сравнение даты, чисел и пустых значений может вернуть false, если фактическим значением является строка. Вы можете использовать == знак равенства, если тип данных для вас не важен.
Дэвид Аддотай

НЕ ДЕЛАЙТЕ ЭТОГО. Узнайте, как использовать функции высшего порядка, это старый способ ведения дел.
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
Просто, но примите во внимание производительность при использовании больших наборов данных
gavioto

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 очень жадный, потому что он возвращает только первое совпадение. Если бы он не был жадным, он продолжил бы поиск. Вы думаете о термине «жадный», как он используется в повседневном английском, но в контексте программирования (то есть SO), это противоположно тому, что вы думаете;)
GrayedFox

Спасибо за объяснение @GrayedFox. Моя ссылка на жадность была на самом деле и из программирования, но она была связана с регулярными выражениями, где жадность принимает больше символов, чем не жадность! :-)
Jos

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 Если бы OP проверил 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
Ответ ниже предоставляет больше информации и был опубликован первым.
Александр

Это определенно лучшее решение для этого.
Bebolicious

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'), я исправил это для вас.
Ракета Хазмат

Это есть один момент. Если переменная «не найдена», то у меня есть положительный индекс. И для проверки «не найден» необходимо сравнить, если я === Data.length
Эндрю Д.

Второй момент - если массив содержит неиндексированные пользовательские свойства. Например: var Data [1,2,3,4,5]; Данные [ "тест"] = 7897; Сравните вывод: for (var i in Data) оповещения (Data [i]); и для (var i = 0; i <Data.length; i ++) оповещения (Data [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 или underscorejs.


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

@ ρяσѕρєя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

Хотя этот фрагмент кода может решить вопрос, в том числе объяснение действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код. Также постарайтесь не переполнять ваш код пояснительными комментариями, так как это снижает удобочитаемость кода и пояснений!
До свидания, 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

В качестве альтернативы ответу Аттанасио Руиса из Германии вы можете устранить второй цикл, используя 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.