Найти объект по идентификатору в массиве объектов JavaScript


1547

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

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}, etc.]

Я не могу изменить структуру массива. Мне передают идентификатор 45, и я хочу получить 'bar'для этого объекта в массиве.

Как мне сделать это в JavaScript или с помощью jQuery?

Ответы:


1193

Используйте find()метод:

myArray.find(x => x.id === '45').foo;

От MDN :

find()Метод возвращает первое значение в массиве, если элемент в массиве удовлетворяет условиям функции. В противном случае undefinedвозвращается.


Если вы хотите найти его индекс , используйте findIndex():

myArray.findIndex(x => x.id === '45');

От MDN :

findIndex()Метод возвращает индекс первого элемента в массиве , который удовлетворяет условие тестирования функции. В противном случае -1 возвращается.


Если вы хотите получить массив подходящих элементов, используйте filter()вместо этого метод:

myArray.filter(x => x.id === '45');

Это вернет массив объектов. Если вы хотите получить массив fooсвойств, вы можете сделать это с помощью map()метода:

myArray.filter(x => x.id === '45').map(x => x.foo);

Примечание: такие методы, как find()or filter(), и функции стрелок не поддерживаются более старыми браузерами (например, IE), поэтому, если вы хотите поддерживать эти браузеры, вы должны перенести свой код с помощью Babelполифилом ).


2
Поэтому для нескольких условий тестирования это будет что-то вроде: myArray.find (x => x.id === '45' && x.color == 'red'). Foo
Apqu

2
Для меня лучший ответ на данный момент. Не нуждается ни в jQuery, ни в создании новых вспомогательных массивов.
Канта

myArray.find (x => x.id === '45') не работает на ПК Mac
Говинда Раджбхар

@TJCrowder Я не думаю, что это хорошая идея, чтобы скопировать и вставить полифилы из MDN в ваш код; вместо этого вы должны использовать пакеты npm с полифиллами. Кроме того, Babel включает полифилы для функций ES2015 + в пакет babel-polyfill .
Михал Перлаковский

2
myArray.find (x => x.id === '45'). foo; выдает исключение, если нет объекта с идентификатором '45'.
Фрейзер Киркман

1466

Поскольку вы уже используете jQuery, вы можете использовать функцию grep, которая предназначена для поиска в массиве:

var result = $.grep(myArray, function(e){ return e.id == id; });

В результате получается массив с найденными элементами. Если вы знаете, что объект всегда существует и что он встречается только один раз, вы можете просто использовать его result[0].fooдля получения значения. В противном случае вы должны проверить длину полученного массива. Пример:

if (result.length === 0) {
  // no result found
} else if (result.length === 1) {
  // property found, access the foo property using result[0].foo
} else {
  // multiple items found
}

124
Было бы безопаснее использовать ===вместо ==, чтобы избежать странных проблем с ==оператором JavaScript .
Вики Chijwani

11
@VickyChijwani: Есть ли проблемы при сравнении строки со строкой?
Guffa

38
Ну, если вы абсолютно уверены, что оба e.idи idбудут строками, я думаю, что это нормально использовать ==. Но если вы не уверены, вы можете столкнуться с проблемами (поскольку '' == 0есть, trueно '' === 0есть false). Не говоря уже о том, что ===кажется быстрее ( stackoverflow.com/questions/359494/… ).
Вики Чиджвани

101
В основном я всегда использую, ===потому что это работает точно так же как ==в других языках программирования. Я считаю, ==что в JavaScript не существует.
Вики Чиджвани

6
@de. Многие ответы здесь обеспечивают предполагаемое поведение при поиске уникальных значений; по сути, вы можете узнать их по факту, что они возвращаются или выходят из своего цикла на раннем этапе (или дают команду конструкции более низкого уровня прекратить итерации). См. Ответ ДжаредПара для канонического примера и комментарий Аарония к этому ответу для того же понимания. В общем, люди различают функции «фильтр» и «поиск» таким образом, но терминология различна. Хотя это и более эффективно, это все же линейный поиск, поэтому, если вы хотите использовать хеш-таблицу, см. Ответ Аарона Дигуллы (остерегайтесь импл. Деталей).
TNE

362

Другое решение - создать объект поиска:

var lookup = {};
for (var i = 0, len = array.length; i < len; i++) {
    lookup[array[i].id] = array[i];
}

... now you can use lookup[id]...

Это особенно интересно, если вам нужно сделать много поисков.

Это не потребует намного больше памяти, так как идентификаторы и объекты будут совместно использоваться.


6
Именно то, что я искал. Забавно, как я пытался чрезмерно усложнить это, пытаясь каждый раз проходить по циклу, удаляя каждый элемент из списка, как я нашел, когда мне нужно было только изменить полученные данные из CouchDB и перевести их в формат, который будет полезен для моего необходимо. +1 сэр!
Slickplaid

5
это умно Я не могу себе представить, как другие были убеждены, просматривая массив для каждого использования.
Аладдин Мхемед

4
Пока вы не полагаетесь на порядок свойств: stackoverflow.com/questions/4886314/…
Marle1

Использует перерыв; в цикле хороший вариант / улучшение, если вы знаете, что есть только один объект для поиска?
irJvV

7
@irJvV: Нет, это не имеет никакого смысла. Приведенный выше код полезен, если вам нужно сделать много поисков. Если вы посмотрите только один раз, то создание lookupобъекта - пустая трата времени.
Аарон Дигулла

174

ECMAScript 2015 предоставляет метод find () для массивов:

var myArray = [
 {id:1, name:"bob"},
 {id:2, name:"dan"},
 {id:3, name:"barb"},
]

// grab the Array item which matchs the id "2"
var item = myArray.find(item => item.id === 2);

// print
console.log(item.name);

Работает без внешних библиотек. Но если вам нужна поддержка старых браузеров, вы можете включить этот полифилл .


1
Вероятно, потому что это все еще кажется очень экспериментальным, и не многие браузеры поддерживают его, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
lejonl

2
Это может быть упрощено до myArray.find(d=>d.id===45).foo;.
Лохматый

1
@ Шэгги или даже myArray.find(({ id }) => id === 45).foo. Но это старый ответ, который был написан до того, как синтаксис ES2015 был так же хорошо поддержан, как и сейчас. @ Gothdo в ответ в настоящее время является наиболее актуальной в потоке.
Рунар Берг

1
@ Shaggy, если .find () возвращает undefined, ваша оптимизация выдает ошибку. Таким образом, это решение может быть использовано только в тех случаях, когда совпадение гарантировано.
Герберт Питерс

1
@HerbertPeters Если вы хотите быть уверен , что вы можете Alway нуль-чек, который будет очень легко с опциональной цепочкой : myArray.find(d => d.id === 45)?.foo.
Рунар Берг

141

У Underscore.js есть хороший метод для этого:

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'},etc.]
obj = _.find(myArray, function(obj) { return obj.id == '45' })

42
Для записи, Lo-Dash (который часто демонстрирует большую производительность, чем Underscore) имеет аналогичный метод. Документы здесь: lodash.com/docs#find
user456584

Если вы ожидаете только один объект, то использование findWhere будет более эффективным, так как после поиска одного результата поиск не пойдет дальше.
навсегда

@Foreever Из документов _.find: «Функция возвращается, как только находит приемлемый элемент, и не пересекает весь список».
GijsjanB

129

Я думаю, что самый простой способ будет следующим, но он не будет работать в Internet Explorer 8 (или ранее):

var result = myArray.filter(function(v) {
    return v.id === '45'; // Filter out the appropriate one
})[0].foo; // Get result and access the foo property

Мне любопытно, есть ли здесь какое-то преимущество в производительности по сравнению с обычным for?
Игорь Зиновьев

@ Игорь Зиновьев: Да, с этими инструментами массива ES5, безусловно, сказывается производительность. Отдельная функция выполняется для каждого элемента, поэтому она не будет очень быстрой по сравнению с прямым forциклом.
pimvdb

Так ты говоришь, что это будет медленнее? Кроме того, он всегда будет сканировать весь массив, насколько я вижу, тогда как forцикл завершится при первом совпадении.
Игорь Зиновьев

Если вам нужна поддержка IE8, просто напишите об этом: stackoverflow.com/questions/7153470/…
Адам Грант

Этот код выдаст ошибку, если с этим нет элементаid
Stan

71

Попробуйте следующее

function findById(source, id) {
  for (var i = 0; i < source.length; i++) {
    if (source[i].id === id) {
      return source[i];
    }
  }
  throw "Couldn't find object with id: " + id;
}

17
Это не заслуживало отдельного ответа, но в современных браузерах это решение можно записать так: jsfiddle.net/rwaldron/j3vST
Рик,

12
Если вы хотите повысить эффективность, обратите внимание, что этот пример, вероятно, быстрее, чем использование filter () (см. Пример Рика), поскольку этот возвращается, как только он находит первый соответствующий элемент, тогда как filter () продолжает работать через весь массив, даже после нахождения матч. У этого также нет стоимости создания дополнительного массива или вызова функции для каждого элемента.
Аароний

3
@Rick, самое интересное в этом ответе, очевидно, что вы можете добавить консоль firebug в окно вывода в jsFiddle. Это намного лучше, чем регистрировать и говорить кому-то еще, чтобы открыть консоль, чтобы увидеть результат. Потрясающие!
KyleMit

1
Пока никто не упомянул об этом, я хотел добавить, что AngularJS также имеет метод фильтра .
Ино



31

Общая и более гибкая версия функции findById выше:

// array = [{key:value},{key:value}]
function objectFindByKey(array, key, value) {
    for (var i = 0; i < array.length; i++) {
        if (array[i][key] === value) {
            return array[i];
        }
    }
    return null;
}

var array = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];
var result_obj = objectFindByKey(array, 'id', '45');

15

Вы можете легко получить это, используя функцию map () :

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];

var found = $.map(myArray, function(val) {
    return val.id == 45 ? val.foo : null;
});

//found[0] == "bar";

Рабочий пример: http://jsfiddle.net/hunter/Pxaua/


1
Я забыл о том, что jQuery mapавтоматически удаляет nullэлементы. Это звучит вводящим в заблуждение для меня и для общей концепции map, поскольку результат не имеет такой же длины оригинальной коллекции.
MaxArt

14

Вы можете использовать фильтры,

  function getById(id, myArray) {
    return myArray.filter(function(obj) {
      if(obj.id == id) {
        return obj 
      }
    })[0]
  }

get_my_obj = getById(73, myArray);

1
@TobiasBeuving - тот, кто использует Array.find (), тоже простой JS и должен останавливаться при первом поиске, поэтому будет более эффективным.
Адриан Линч

12

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

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

var items = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];

var lookup = items.reduce((o,i)=>o[i.id]=o,{});

затем вы можете получить предметы в определенное время, как это:

var bar = o[id];

Вы можете также рассмотреть возможность использования карты вместо объекта в качестве поиска: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map


11

Используя родной Array.reduce

var array = [ {'id':'73' ,'foo':'bar'} , {'id':'45' ,'foo':'bar'} , ];
var id = 73;
var found = array.reduce(function(a, b){
    return (a.id==id && a) || (b.id == id && b)
});

возвращает элемент объекта, если найден, в противном случае false


Просто обратите внимание, Array.reduce не поддерживается в IE8 и ниже.
Burn_E99

7

Если вы делаете это несколько раз, вы можете настроить карту (ES6):

const map = new Map( myArray.map(el => [el.id, el]) );

Тогда вы можете просто сделать:

map.get(27).foo

6

Вот как я могу это сделать в чистом JavaScript, самым минимальным образом, который я могу себе представить, работает в ECMAScript 3 или более поздней версии. Он возвращается, как только совпадение найдено.

var getKeyValueById = function(array, key, id) {
    var testArray = array.slice(), test;
    while(test = testArray.pop()) {
        if (test.id === id) {
            return test[key];
        }
    }
    // return undefined if no matching id is found in array
    return;
}

var myArray = [{'id':'73', 'foo':'bar'}, {'id':'45', 'foo':'bar'}]
var result = getKeyValueById(myArray, 'foo', '45');

// result is 'bar', obtained from object with id of '45'

5

Более общий и короткий

function findFromArray(array,key,value) {
        return array.filter(function (element) {
            return element[key] == value;
        }).shift();
}

в вашем случае Ex. var element = findFromArray(myArray,'id',45)это даст вам весь элемент.


4

Вы можете попробовать Sugarjs с http://sugarjs.com/ .

У него очень приятный метод для массивов .find. Таким образом, вы можете найти такой элемент:

array.find( {id: 75} );

Вы также можете передать объект с дополнительными свойствами, чтобы добавить еще одно "where-предложение".

Обратите внимание, что Sugarjs расширяет родные объекты, и некоторые люди считают это очень злым ...


2
Ну, это зло, так как это может случиться так, что новые версии ECMAScript могут ввести новые методы с тем же именем. И угадайте что, это именно то, что случилось сfind . Я предлагаю, чтобы, если вы хотите расширить собственные прототипы, всегда используйте более конкретные имена, оставляя самые простые из них для будущих стандартных разработок.
MaxArt

этому комментарию почти 2 года, и сегодня я все равно предпочел бы использовать lodash. Однако, если вы хотите, вы можете прочитать об этой теме на веб-сайте sugarjs. Они придерживаются вашего мнения: sugarjs.com/native
deepflame

1
Оператор специально просил Javascript или JQuery решение
Тобиас Бивинг

4

Опираясь на принятый ответ:

JQuery:

var foo = $.grep(myArray, function(e){ return e.id === foo_id})
myArray.pop(foo)

Или CoffeeScript:

foo = $.grep myArray, (e) -> e.id == foo_id
myArray.pop foo

4

В последнее время мне приходится сталкиваться с тем же, что и при поиске строки из огромного массива.

После некоторого поиска я обнаружил, что это будет легко сделать с помощью простого кода:

Код:

var items = mydata.filter(function(item){
    return item.word.toLowerCase().startsWith( 'gk );
})

См. Https://jsfiddle.net/maheshwaghmare/cfx3p40v/4/

Серач из 20к строк


3

Итерировать по любому элементу в массиве. Для каждого элемента, который вы посещаете, проверьте его идентификатор. Если это совпадение, верните его.

Если вы просто хотите код:

function getId(array, id) {
    for (var i = 0, len = array.length; i < len; i++) {
        if (array[i].id === id) {
            return array[i];
        }
    }
    return null; // Nothing found
}

И то же самое, используя методы массива ECMAScript 5:

function getId(array, id) {
    var obj = array.filter(function (val) {
        return val.id === id;
    });

    // Filter returns an array, and we just want the matching item.
    return obj[0];
}

3

Пока браузер поддерживает ECMA-262 , 5-е издание (декабрь 2009 г.), это должно работать почти в одну строку:

var bFound = myArray.some(function (obj) {
    return obj.id === 45;
});

2
Почти. bFoundэто просто логическое значение, trueесли элемент удовлетворяет требуемому условию.
MaxArt

3

Вы можете сделать это даже в чистом JavaScript, используя встроенную функцию «filter» для массивов:

Array.prototype.filterObjects = function(key, value) {
    return this.filter(function(x) { return x[key] === value; })
}

Так что теперь просто передайте "id" вместо keyи "45" вместо value, и вы получите полный объект, соответствующий идентификатору 45. Так что,

myArr.filterObjects("id", "45");

16
Не изменяйте объекты, которые вам не принадлежат.
Михал Перлаковский

3

использование Array.prototype.filter() функцию.

ДЕМО : https://jsfiddle.net/sumitridhal/r0cz0w5o/4/

JSON

var jsonObj =[
 {
  "name": "Me",
  "info": {
   "age": "15",
   "favColor": "Green",
   "pets": true
  }
 },
 {
  "name": "Alex",
  "info": {
   "age": "16",
   "favColor": "orange",
   "pets": false
  }
 },
{
  "name": "Kyle",
  "info": {
   "age": "15",
   "favColor": "Blue",
   "pets": false
  }
 }
];

ФИЛЬТР

var getPerson = function(name){
    return jsonObj.filter(function(obj) {
      return obj.name === name;
    });
}

как я могу искать внутри вложенного объекта? Как домашние животные = ложь должен вернуть два объекта.
Валай

использовать .filterметод obj.infoво вложенном цикле. var getPerson = function(name){ return jsonObj.filter(function(obj) { return obj.info.filter(function(info) { return pets === false; }); }); }
Сумит Ридхал

Вы также можете использовать стиль es6 imo ... const filterData = jsonObj.filter (obj => obj.name === 'Alex')
DagicCross

3

Мы можем использовать методы Jquery $.each()/$.grep()

var data= [];
$.each(array,function(i){if(n !== 5 && i > 4){data.push(item)}}

или

var data = $.grep(array, function( n, i ) {
  return ( n !== 5 && i > 4 );
});

использовать синтаксис ES6:

Array.find, Array.filter, Array.forEach, Array.map

Или используйте Lodash https://lodash.com/docs/4.17.10#filter , Подчеркните https://underscorejs.org/#filter


2

Мне очень понравился ответ, предоставленный Аароном Дигуллой, но мне нужно было сохранить массив объектов, чтобы я мог повторить его позже. Поэтому я изменил это

	var indexer = {};
	for (var i = 0; i < array.length; i++) {
	    indexer[array[i].id] = parseInt(i);
	}
	
	//Then you can access object properties in your array using 
	array[indexer[id]].property


Используется то же решение, что и самое быстрое для поиска элементов в массиве. Но parseInt здесь избыточен.
aleha

1

Использование:

var retObj ={};
$.each(ArrayOfObjects, function (index, obj) {

        if (obj.id === '5') { // id.toString() if it is int

            retObj = obj;
            return false;
        }
    });
return retObj;

Он должен вернуть объект по id.


Вы могли бы сократить свой код, используя return obj.id === 5? obj: false; Я много использую $ .each для перебора массивов.
Marcel

@ Marcel: Это не сработает. Так как возвращение false завершит цикл, он найдет объект, только если он был первым элементом в массиве.
Guffa

1

Это решение может также помочь:

Array.prototype.grep = function (key, value) {
    var that = this, ret = [];
    this.forEach(function (elem, index) {
        if (elem[key] === value) {
            ret.push(that[index]);
        }
    });
    return ret.length < 2 ? ret[0] : ret;
};
var bar = myArray.grep("id","45");

Я сделал это точно так же, $.grepи если один объект будет найден, функция вернет объект, а не массив.


2
Не изменяйте объекты, которые вам не принадлежат.
Михал Перлаковский

@ Да, я согласен. Если кто-то не знал, function will return the object, rather than an arrayможет получить ошибку, но я думаю, что это зависит от пользователей.
Соя

0

Начиная от ответа aggaton в это функция , которая на самом деле возвращает элемент хотел (или , nullесли не найден), учитывая arrayи на callbackфункцию , которая возвращает значение truthy для «правильного» элемента:

function findElement(array, callback) {
    var elem;
    return array.some(function(e) {
        if (callback(e)) {
            elem = e;
            return true;
        }
    }) ? elem : null;
});

Просто помните, что это не работает на IE8, так как не поддерживает some. Может быть предоставлен polyfill, или всегда есть классический forцикл:

function findElement(array, callback) {
    for (var i = 0; i < array.length; i++)
        if (callback(array[i])) return array[i];
    return null;
});

Это на самом деле быстрее и компактнее. Но если вы не хотите изобретать велосипед, я предлагаю использовать служебную библиотеку, например, подчеркивание или lodash.


0

Кратчайший,

var theAnswerObj = _.findWhere(array, {id : 42});

1
Для этого необходимо использовать библиотеку подчеркивания, OP попросил простое решение javascript или jQuery
Tobias Beuving

2
как только вы добавите подчеркивание, это не короткий ответ!
Тим Огилви

-1

Рассмотрим «axesOptions» как массив объектов с форматом объекта {: field_type => 2,: fields => [1,3,4]}

function getFieldOptions(axesOptions,choice){
  var fields=[]
  axesOptions.each(function(item){
    if(item.field_type == choice)
        fields= hashToArray(item.fields)
  });
  return fields;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.