Как найти значение в массиве объектов в JavaScript?

91

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

Object = {
   1 : { name : bob , dinner : pizza },
   2 : { name : john , dinner : sushi },
   3 : { name : larry, dinner : hummus }
}

Я хочу иметь возможность найти в объекте / массиве ключ «ужин» и посмотреть, соответствует ли он «суши».

Я знаю, что в jQuery есть $ .inArray, но он, похоже, не работает с массивами объектов. А может я ошибаюсь. indexOf также, похоже, работает только на одном уровне массива.

Для этого нет функции или существующего кода?

Спрашивающий
источник
Об этом уже спрашивали. Вы должны написать свою собственную функцию или использовать другую библиотеку.
Феликс Клинг,
1
Обратите внимание, что Objectв Javascript зарезервирован Objectобъект-объект, т.е. мать всех объектов.
adamse 03
1
вопрос и принятый ответ не связаны с многомерными массивами, а больше с фильтрацией одномерного массива по значениям свойств его элементов. => Они не решили мою проблему «найти значение в многомерном массиве».
Мартин Шнайдер

Ответы:

213

Если у вас есть такой массив, как

var people = [
  { "name": "bob", "dinner": "pizza" },
  { "name": "john", "dinner": "sushi" },
  { "name": "larry", "dinner": "hummus" }
];

Вы можете использовать filterметод объекта Array:

people.filter(function (person) { return person.dinner == "sushi" });
  // => [{ "name": "john", "dinner": "sushi" }]

В новых реализациях JavaScript вы можете использовать выражение функции:

people.filter(p => p.dinner == "sushi")
  // => [{ "name": "john", "dinner": "sushi" }]

Вы можете искать людей, "dinner": "sushi"использующихmap

people.map(function (person) {
  if (person.dinner == "sushi") {
    return person
  } else {
    return null
  }
}); // => [null, { "name": "john", "dinner": "sushi" }, null]

или reduce

people.reduce(function (sushiPeople, person) {
  if (person.dinner == "sushi") {
    return sushiPeople.concat(person);
  } else {
    return sushiPeople
  }
}, []); // => [{ "name": "john", "dinner": "sushi" }]

Я уверен, что вы сможете обобщить это на произвольные ключи и значения!

Адамс
источник
7
Просто имейте в виду, что эти решения являются частью ECMAScript 5 и не поддерживаются в IE8. kangax.github.com/es5-compat-table Насколько я предпочитаю ответ @adamse, Alex's более дружелюбен к "старому дерьмовому браузеру". Хотя не уверен в производительности.
EasyCo
@SalmanA - ни вопрос, ни решение не относятся к jQuery. Используется javascript filter (), а не специфичный для jQuery $ .filter () - tutorialspoint.com/javascript/array_filter.htm
Tapirboy
Как упоминалось EasyCo, функция фильтрации не поддерживается в IE8. Однако его легко добавить к прототипу Array и, следовательно, использовать в любом браузере с небольшой функцией в начале ваших скриптов. Это описано в документации фильтра . Он дает точную функцию, указанную в ECMA-262, так что это буквально одно и то же.
dallin 08
1
Я только что добавил ответ, в котором используется grepметод jQuery . Возможно, имеет смысл включить ваш ответ, поскольку он такой же, как и то, что вы делаете, но зависит от jQuery и дружелюбен к браузеру.
Zach Lysobey
Есть ли причина, по которой я продолжаю получать справочную ошибку с моей возвращаемой переменной? Я попробовал два верхних ответа, возвращающих «x», и он продолжает говорить, что он не определен ...
Эван Лало
18

jQuery имеет встроенный метод, jQuery.grepкоторый работает аналогично filterфункции ES5 из ответа @ adamse и должен нормально работать в старых браузерах.

На примере Адамсе:

var peoples = [
  { "name": "bob", "dinner": "pizza" },
  { "name": "john", "dinner": "sushi" },
  { "name": "larry", "dinner": "hummus" }
];

вы можете сделать следующее

jQuery.grep(peoples, function (person) { return person.dinner == "sushi" });
  // => [{ "name": "john", "dinner": "sushi" }]
Зак Лисоби
источник
10
var getKeyByDinner = function(obj, dinner) {
    var returnKey = -1;

    $.each(obj, function(key, info) {
        if (info.dinner == dinner) {
           returnKey = key;
           return false; 
        };   
    });

    return returnKey;       

}

jsFiddle .

Пока -1не будет действительным ключом.

Алекс
источник
почти поддержал это, но в посте не было объяснений.
mickmackusa
10

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

Obj = { 'pizza' : { 'name' : 'bob' }, 'sushi' : { 'name' : 'john' } }

Теперь вы можете легко получить к нему доступ следующим образом: Object['sushi']['name']

Или, если объект действительно такой простой (просто "имя" в объекте), вы можете просто изменить его на:

Obj = { 'pizza' : 'bob', 'sushi' : 'john' }

А затем получить доступ к нему , как: Object['sushi'].

Очевидно, что не всегда возможно или в ваших интересах реструктурировать ваш объект данных таким образом, но дело в том, что иногда лучший ответ - это подумать о том, структурирован ли ваш объект данных наилучшим образом. Создание такого ключа может быть быстрее и создавать более чистый код.

Даллин
источник
1
Обожаю ответ, но я обнаружил проблему с синтаксисом: мой работал только так: obj = {"pizza": {"name": "bob"}, "sushi": {"name": "john"}} alert ( obj ['пицца'] ['имя']); но все равно. Благодарность! нашел то, что искал! )
aleXela 09
@alexela Спасибо, Алексела! Я обновил свой ответ вашим проницательным наблюдением! Я только что скопировал пример из сообщения OP и не стал добавлять кавычки, но вы правы - это не сработает, если не будут заключены хотя бы кавычки вокруг значений (при условии, что это значения, а не переменные).
dallin
3

Найти объект в массиве можно с помощью библиотеки Alasql :

var data = [ { name : "bob" , dinner : "pizza" }, { name : "john" , dinner : "sushi" },
     { name : "larry", dinner : "hummus" } ];

var res = alasql('SELECT * FROM ? WHERE dinner="sushi"',[data]);

Попробуйте этот пример в jsFiddle .

Agershun
источник
3
Я не уверен, что это действительно заслуживает отрицательного голосования. Sql-подобные запросы к коллекциям становятся намного проще рассуждать и писать, когда требования становятся более сложными, чем один фильтр или вызов reduce. Alasql - довольно впечатляющая библиотека, но, по общему признанию, немного избыточна для приведенного выше примера.
TomDotTom
1
если сам объект произошел из источника SQL, тогда это гениальный ответ.
edwardsmarkf
1

Вы можете использовать простой цикл for:

for (prop in Obj){
    if (Obj[prop]['dinner'] === 'sushi'){

        // Do stuff with found object. E.g. put it into an array:
        arrFoo.push(Obj[prop]);
    }
}

В следующем примере скрипта все содержащиеся объекты помещаются dinner:sushiв массив:

https://jsfiddle.net/3asvkLn6/1/

Ротарети
источник
1

Здесь уже есть много хороших ответов, так почему бы не еще один, используйте библиотеку, такую ​​как lodash или подчеркивание :)

obj = {
   1 : { name : 'bob' , dinner : 'pizza' },
   2 : { name : 'john' , dinner : 'sushi' },
   3 : { name : 'larry', dinner : 'hummus' }
}

_.where(obj, {dinner: 'pizza'})
>> [{"name":"bob","dinner":"pizza"}]
TomDotTom
источник
0

Мне пришлось искать во вложенной структуре карты сайта первый элемент, который проходит по заданному пути. Я придумал следующий код, просто используя .map() .filter()и .reduce. Возвращает последний найденный элемент, соответствующий пути /c.

var sitemap = {
  nodes: [
    {
      items: [{ path: "/a" }, { path: "/b" }]
    },
    {
      items: [{ path: "/c" }, { path: "/d" }]
    },
    {
      items: [{ path: "/c" }, { path: "/d" }]
    }
  ]
};

const item = sitemap.nodes
  .map(n => n.items.filter(i => i.path === "/c"))
  .reduce((last, now) => last.concat(now))
  .reduce((last, now) => now);

Редактировать 4n4904z07

Марк
источник
0

Если вы хотите найти конкретный объект с помощью функции поиска, просто попробуйте что-то вроде этого:

    function findArray(value){

        let countLayer = dataLayer.length;
        for(var x = 0 ; x < countLayer ; x++){

            if(dataLayer[x].user){
                let newArr = dataLayer[x].user;
                let data = newArr[value];
                return data;
            }

        }

        return null;

    }

    findArray("id");

Это пример объекта:

layerObj = {
    0: { gtm.start :1232542, event: "gtm.js"},
    1: { event: "gtm.dom", gtm.uniqueEventId: 52},
    2: { visitor id: "abcdef2345"},
    3: { user: { id: "29857239", verified: "Null", user_profile: "Personal", billing_subscription: "True", partners_user: "adobe"}
}

Код выполнит итерацию и найдет «пользовательский» массив и будет искать объект, который вы ищете внутри.

Моя проблема заключалась в том, что индекс массива менялся при каждом обновлении окна, и он был либо в третьем, либо во втором массиве, но это не имеет значения.

Работал как шарм для Меня!

В вашем примере это немного короче:

function findArray(value){

    let countLayer = Object.length;
    for(var x = 0 ; x < countLayer ; x++){

        if(Object[x].dinner === value){
            return Object[x];
        }

    }

    return null;

}

findArray('sushi');
z3r0
источник
0

Я бы старался не изобретать велосипед. Мы используем сканирование объектов для всех наших потребностей в обработке данных. Концептуально это очень просто, но позволяет делать много интересных вещей. Вот как вы решите свой конкретный вопрос

const objectScan = require('object-scan');

const findDinner = (dinner, data) => objectScan(['*'], {
  abort: true,
  rtn: 'value',
  filterFn: ({ value }) => value.dinner === dinner
})(data);

const data = {
  1: { name: 'bob', dinner: 'pizza' },
  2: { name: 'john', dinner: 'sushi' },
  3: { name: 'larry', dinner: 'hummus' }
};

console.log(findDinner('sushi', data));
// => { name: 'john', dinner: 'sushi' }
Винсент
источник