Как определить, содержит ли массив Javascript объект с атрибутом, равным заданному значению?

659

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

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    } //and so on goes array... 
];

Как проверить этот массив, чтобы увидеть, существует ли Magenic? Я не хочу зацикливаться, если я не должен. Я работаю с потенциально несколькими тысячами записей.


ОБНОВЛЕНО

Поскольку это был популярный пост, я решил поделиться чем-то новым, что нашел. И, кажется, @CAFxX уже поделился этим! Я должен читать это чаще. Я наткнулся на https://benfrain.com/understanding-native-javascript-array-methods/ .

vendors.filter(function(vendor){ return vendor.Name === "Magenic" })

А с ECMAScript 2015 стало еще проще использовать новые функции стрелок:

vendors.filter(vendor => vendor.Name === "Magenic")
Дэвид Лоцци
источник
Прошу прощения за кажущийся случайным комментарий, но ваш вопрос касался JSON или просто массивов JavaScript?
Алекс Турпин
4
Решение @CAFxX лучше, было бы здорово, если вы обновите выбранное решение.
eMarine
1
Договорились, раньше этого не видели!
Дэвид Лоцци
Теперь вы можете упростить это еще больше, используя функции стрелок. Все современные браузеры поддерживают это и выглядят лучше.
Петр Кула
Вы можете использовать функцию карты, очень
полезную

Ответы:

264

Редактирование 2018 года : этот ответ с 2011 года, до того, как браузеры широко поддерживали методы фильтрации массивов и функции стрелок. Посмотрите на ответ CAFxX .

Не существует «волшебного» способа проверить что-либо в массиве без цикла. Даже если вы используете какую-то функцию, сама функция будет использовать цикл. Что вы можете сделать, так это выйти из цикла, как только вы найдете то, что ищете, чтобы минимизировать время вычислений.

var found = false;
for(var i = 0; i < vendors.length; i++) {
    if (vendors[i].Name == 'Magenic') {
        found = true;
        break;
    }
}
Алекс Турпин
источник
4
Нет проблем. Имейте в виду, что решение Кейта также очень жизнеспособно и спасает вас от зацикливания.
Алекс Турпин
2
Вам не нужен флаг, если все, что вам нужно знать, это то, находится ли «что-то» или нет, вы можете просто проверить значение индекса сканирования с размером массива. Чтобы это работало, индекс var должен быть объявлен перед оператором for.
Алекс
5
Эти параметры теперь работают: vendors.forEach, vendors.filter, vendors.reduce
Дэвид Лоззи,
1
А как насчет JSON.stringify (vendors) .indexOf ('Magenic')! == -1
Последнее дыхание,
2
@LastBreath, который может легко привести к ложному срабатыванию, если он 'Magenic'находится где-то еще в объекте
Алекс Турпин
950

Не нужно изобретать рулевое колесоцикл, по крайней мере, явно (с использованием функций стрелок , только в современных браузерах ):

if (vendors.filter(e => e.Name === 'Magenic').length > 0) {
  /* vendors contains the element we're looking for */
}

или еще лучше :

if (vendors.some(e => e.Name === 'Magenic')) {
  /* vendors contains the element we're looking for */
}

РЕДАКТИРОВАТЬ: Если вам нужна совместимость с паршивыми браузерами, то ваш лучший выбор:

if (vendors.filter(function(e) { return e.Name === 'Magenic'; }).length > 0) {
  /* vendors contains the element we're looking for */
}
CAFxX
источник
4
@Rocket, почему ты отредактировал мой ответ? Синтаксис без фигурных скобок является совершенно допустимым JavaScript .
CAFxX
4
Синтаксис "лямбда" все еще не работает в Chrome 16 (который не является паршивым браузером).
Ракета Хазмат
27
Думаю, это зависит от вашего определения паршивости. Этот синтаксис является частью JavaScript 1.8.
CAFxX
7
В выражении укупорочных вы используете здесь в первом и вторых примерах имеют нестандартный не использовать! предупреждение от Mozilla (см. эту ссылку). Они когда-либо работали только в Firefox, и теперь устарели и будут удалены в пользу функций стрелок .
Доппельгринер
2
@ 7hibault, потому что someможет вызвать короткое замыкание, когда объект с name === "Magenic"найден. С помощью filterон будет проверять каждый элемент до конца массива и создавать новые элементы массива, которые соответствуют условию, а затем проверятьlength
adiga
93

Нет необходимости петли. Три метода, которые приходят на ум:

Array.prototype.some ()

Это самый точный ответ на ваш вопрос, то есть «проверьте, если что-то существует», что подразумевает результат bool. Это будет верно, если есть какие-либо «магические» объекты, иначе ложь:

let hasMagenicVendor = vendors.some( vendor => vendor['Name'] === 'Magenic' )

Array.prototype.filter ()

Это вернет массив всех объектов Magenic, даже если он есть только один (вернет одноэлементный массив):

let magenicVendors = vendors.filter( vendor => vendor['Name'] === 'Magenic' )

Если вы попытаетесь привести это к логическому значению, оно не будет работать, так как пустой массив (без объектов Magenic) все еще верен. Так что просто используйте magenicVendors.lengthв своих условных.

Array.prototype.find ()

Это вернет первый Magenic-объект (или, undefinedесли его нет):

let magenicVendor = vendors.find( vendor => vendor['Name'] === 'Magenic' );

Это приводит к булеву окей (любой объект правдив, undefinedфальшив).


Примечание: я использую vendor ["Name"] вместо vendor.Name из-за странного расположения имен свойств.

Примечание 2: Нет причин использовать свободное равенство (==) вместо строгого равенства (===) при проверке имени.

boxtrain
источник
5
Полезно отметить, что под капотом все эти петли. Они также все медленнее в вычислительном отношении, чем просто для цикла и выполнения операций.
ThePartyTurtle
Можно также поделиться этой любовью здесь: stackoverflow.com/questions/21748670/… чтобы больше таких людей, как я, не переходили на эту старую страницу и не делали предположений.
ThePartyTurtle
43

Принятый ответ все еще работает, но теперь у нас есть собственный метод ECMAScript 6 [Array.find][1] для достижения того же эффекта.

Цитируя MDN:

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

var arr = []; 
var item = {
  id: '21',
  step: 'step2',
  label: 'Banana',
  price: '19$'
};

arr.push(item);
/* note : data is the actual object that matched search criteria 
  or undefined if nothing matched */
var data = arr.find( function( ele ) { 
    return ele.id === '21';
} );

if( data ) {
 console.log( 'found' );
 console.log(data); // This is entire object i.e. `item` not boolean
}

Смотрите мою ссылку на jsfiddle. Существует полифил для IE, предоставляемый mozilla.

TeaCoder
источник
2
Может быть короче, если вы просто делаете return ele.id == '2', но +1 за хорошее решение ES6.
Lye Fish
Хорошо иметь свежий ответ :) Просто интересно, лучше ли производительность или нет, чем ответы выше ...
Emidomenge
Я думаю, что важно указать, что возвращаемое значение 'data' (когда ele.id соответствует идентификатору, например, '21') будет представлять собой сам элемент массива (в данном случае, весь объект item). Если бы ожидалось, что результатом переменной данных будет «true» или «false» вместо ложного значения, вы будете крайне разочарованы.
Адамгеде
Спасибо! Моя задача была немного другой. Получить индекс Object в массиве => push if <0 || splice(index, 1)вот мой немного обновленный код:const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id))
Леонид Задорожных
30

Вот как я это сделаю

const found = vendors.some(item => item.Name === 'Magenic');

array.some()Метод проверяет, есть ли хотя бы одно значение в массиве, которое соответствует критериям, и возвращает логическое значение. С этого момента вы можете пойти с:

if (found) {
// do something
} else {
// do something else
}
Мирза Лека
источник
22

Если вы не хотите реструктурировать это так:

vendors = {
    Magenic: {
      Name: 'Magenic',
      ID: 'ABC'
     },
    Microsoft: {
      Name: 'Microsoft',
      ID: 'DEF'
    } and so on... 
};

на что ты можешь if(vendors.Magnetic)

Вам придется зациклить

Keith.Abramo
источник
2
В случае, если он все еще хотел сохранить структуру объекта, чтобы использовать ее в другом месте
Кит.Абрамо
21

Согласно спецификации ECMAScript 6, вы можете использовать findIndex.

const magenicIndex = vendors.findIndex(vendor => vendor.Name === 'Magenic');

magenicIndexбудет содержать 0( или это индекс в массиве), или -1если он не был найден.

Лукас Бенто
источник
Просто чтобы люди знали, что 0 все равно будет совпадать как ложный результат, если это будет использоваться в качестве условия. По этой причине я думаю, что find () лучше, поскольку вы получаете более логичную правдивую оценку.
dhj
15

Как ОП задал вопрос, существует ли ключ или нет .

Более элегантное решение, которое будет возвращать логическое значение с использованием функции сокращения ES6, может быть

const magenicVendorExists =  vendors.reduce((accumulator, vendor) => (accumulator||vendor.Name === "Magenic"), false);

Примечание . Начальным параметром Reduce является a, falseи если массив имеет ключ, он вернет true.

Надеюсь, это поможет для лучшей и чистой реализации кода

Джей Чакра
источник
1
С каких пор !! [] равно false?
Сергей
1
Хорошо поймал. Обновленный ответ с использованием уменьшения :)
Джей Чакра
1
Это не верно. Первым параметром reduceявляется аккумулятор, а не vendorобъект. Это проверяет false.Name === "Magenic"каждый цикл и возвращает false
adiga
@adiga: исправлено.
Джей Чакра
1
Пожалуйста, также проверьте решение Мирзы Лека. Гораздо более элегантное решение.
Джей Чакра
13

Вы не можете, не глядя на объект на самом деле.

Вы, вероятно, должны немного изменить свою структуру, как

vendors = {
    Magenic:   'ABC',
    Microsoft: 'DEF'
};

Тогда вы можете просто использовать его как хеш поиска.

vendors['Microsoft']; // 'DEF'
vendors['Apple']; // undefined
JANDY
источник
6

Может быть слишком поздно, но массив JavaScript имеет два метода someиevery метод, который возвращает логическое значение и может помочь вам достичь этого.

Я думаю, someчто будет наиболее подходящим для того, что вы намерены достичь.

vendors.some( vendor => vendor['Name'] !== 'Magenic' )

Некоторые подтверждают, что любой из объектов в массиве удовлетворяет заданному условию.

vendors.every( vendor => vendor['Name'] !== 'Magenic' )

Каждый проверяет, что все объекты в массиве удовлетворяют заданному условию.

Акинджиола Тони
источник
Это не работает, const array1 = [{name:'Mike'},{name:'Alice'}]; console.log(array1.every(item => item.name !== 'Mike'));оно должно вернуть истину
Thanwa Ch.
Извини приятель, я имел ввиду some, обновлю мой ответ.
Акинджиола Тони
5

Вы должны зацикливаться, нет никакого способа обойти это.

function seekVendor(vendors, name) {
  for (var i=0, l=vendors.length; i<l; i++) {
    if (typeof vendors[i] == "object" && vendors[i].Name === name) {
      return vendors[i];
    }
  }
}

Конечно, вы можете использовать библиотеку вроде linq.js, чтобы сделать это более приятным:

Enumerable.From(vendors).Where("$.Name == 'Magenic'").First();

(см. jsFiddle для демонстрации)

Я сомневаюсь, что linq.js будет быстрее, чем прямой цикл, но он, безусловно, будет более гибким, когда все немного сложнее.

Томалак
источник
5

Тестирование для элементов массива:

JS предлагает функции массива, которые позволяют вам достичь этого относительно легко. Они следующие:

  1. Array.prototype.filter: Принимает функцию обратного вызова, которая является тестом, затем массив перебирается с обратным вызовом и фильтруется в соответствии с этим обратным вызовом. Новый отфильтрованный массив возвращается.
  2. Array.prototype.some: Принимает функцию обратного вызова, которая является тестом, затем массив перебирается с помощью функции обратного вызова, и если какой-либо элемент проходит тест, возвращается логическое значение true. В противном случае возвращается false

Особенности лучше всего объяснить на примере:

Пример:

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    } //and so on goes array... 
];

// filter returns a new array, we instantly check if the length 
// is longer than zero of this newly created array
if (vendors.filter(company => company.Name === 'Magenic').length ) {
  console.log('I contain Magenic');
}

// some would be a better option then filter since it directly returns a boolean
if (vendors.some(company => company.Name === 'Magenic')) {
  console.log('I also contain Magenic');
}

Поддержка браузера:

Эти 2 функции являются ES6функциональными, не все браузеры могут их поддерживать. Чтобы преодолеть это, вы можете использовать полифилл. Вот полифилл для Array.prototype.some(из MDN):

if (!Array.prototype.some) {
  Array.prototype.some = function(fun, thisArg) {
    'use strict';

    if (this == null) {
      throw new TypeError('Array.prototype.some called on null or undefined');
    }

    if (typeof fun !== 'function') {
      throw new TypeError();
    }

    var t = Object(this);
    var len = t.length >>> 0;

    for (var i = 0; i < len; i++) {
      if (i in t && fun.call(thisArg, t[i], i, t)) {
        return true;
      }
    }

    return false;
  };
}

Виллем ван дер Веен
источник
4

если вы используете jquery, вы можете воспользоваться grep для создания массива со всеми соответствующими объектами:

var results = $.grep(vendors, function (e) {
    return e.Name == "Magenic";
});

а затем используйте массив результатов:

for (var i=0, l=results.length; i<l; i++) {
    console.log(results[i].ID);
}
Эйтан
источник
3

Поправьте меня, если я ошибаюсь ... я мог бы использовать forEachтакой метод,

var found=false;
vendors.forEach(function(item){
   if(item.name === "name"){
       found=true;

   }
});

В настоящее время я привык к этому, из-за его простоты и понятного слова. Спасибо.

Сиддеш Мишра
источник
1
Примечание: здесь нет смысла возвращаться
Edison
2

Вы можете попробовать это для меня.

const _ = require('lodash');

var arr = [
  {
    name: 'Jack',
    id: 1
  },
  {
    name: 'Gabriel',
    id: 2
  },
  {
    name: 'John',
    id: 3
  }
]

function findValue(arr,value) {
  return _.filter(arr, function (object) {
    return object['name'].toLowerCase().indexOf(value.toLowerCase()) >= 0;
  });
}

console.log(findValue(arr,'jack'))
//[ { name: 'Jack', id: 1 } ]
Жениш
источник
Ну, это действительно старый вопрос, и я думаю, что его обновление уже имеет лучшее решение в настоящее время.
Федерико Гальфионе
1

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

let newArray = filter(_this.props.ArrayOne, function(item) {
                    return find(_this.props.ArrayTwo, {"speciesId": item.speciesId});
                });

Это всего лишь один из способов сделать это. Еще один может быть:

var newArray=  [];
     _.filter(ArrayOne, function(item) {
                        return AllSpecies.forEach(function(cItem){
                            if (cItem.speciesId == item.speciesId){
                            newArray.push(item);
                          }
                        }) 
                    });

console.log(arr);

Приведенный выше пример также можно переписать без использования таких библиотек, как:

var newArray=  [];
ArrayOne.filter(function(item) {
                return ArrayTwo.forEach(function(cItem){
                    if (cItem.speciesId == item.speciesId){
                    newArray.push(item);
                  }
                }) 
            });
console.log(arr);

Надеюсь, мой ответ поможет.

Абхай Широ
источник
1

Многие ответы здесь хороши и довольно просты. Но если ваш массив объектов имеет фиксированный набор значений, вы можете использовать следующий трюк:

Сопоставьте все имя в объекте.

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    }
];

var dirtyObj = {}
for(var count=0;count<vendors.length;count++){
   dirtyObj[vendors[count].Name] = true //or assign which gives you true.
}

Теперь этот dirtyObj можно использовать снова и снова безо всякого цикла.

if(dirtyObj[vendor.Name]){
  console.log("Hey! I am available.");
}
jesusverma
источник
1

Чтобы сравнить один объект с другим, я объединяю цикл for (используется для циклического перемещения по объектам) и some (). Вам не нужно беспокоиться о выходе массива за пределы границ и т. Д., Чтобы сохранить некоторый код. Документацию по .some можно найти здесь

var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];    
var  objectsFound = [];

for(let objectNumber in productList){
    var currentId = productList[objectNumber].id;   
    if (theDatabaseList.some(obj => obj.id === currentId)) {
        // Do what you need to do with the matching value here
        objectsFound.push(currentId);
    }
}
console.log(objectsFound);

Альтернативный способ сравнения одного объекта с другим - использовать вложенный цикл for с Object.keys (). Length, чтобы получить количество объектов в массиве. Код ниже:

var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];    
var objectsFound = [];

for(var i = 0; i < Object.keys(productList).length; i++){
        for(var j = 0; j < Object.keys(theDatabaseList).length; j++){
        if(productList[i].id === theDatabaseList[j].id){
            objectsFound.push(productList[i].id);
        }       
    }
}
console.log(objectsFound);

Чтобы ответить на ваш точный вопрос, если вы просто ищете значение в объекте, вы можете использовать один цикл for.

var vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    } 
];

for(var ojectNumbers in vendors){
    if(vendors[ojectNumbers].Name === 'Magenic'){
        console.log('object contains Magenic');
    }
}
стеклярус
источник
0

В качестве альтернативы вы можете сделать:

const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));
BrunoWest
источник
1
Вы бы лучше сказали, почему он может это сделать
Аззаби Хайтем,
0

var without2 = (arr, args) => arr.filter(v => v.id !== args.id); Пример:

without2([{id:1},{id:1},{id:2}],{id:2})

Результат: без 2 ([{id: 1}, {id: 1}, {id: 2}], {id: 2})

бехзад аббаси
источник
Я думаю, что вы хотели сказать Результат: [{id: 1}, {id: 1}]
Исаак Пак
0
const a = [{one:2},{two:2},{two:4}]
const b = a.filter(val => "two" in val).length;
if (b) {
   ...
}
user1665355
источник
3
Пожалуйста, немного описания и убедитесь, что приведенный вами пример работает .. (фильтр не изменит исходный массив, а клонирует его).
Моше Симантов
-1

Мой подход к решению этой проблемы заключается в использовании ES6 и создании функции, которая выполняет проверку за нас. Преимущество этой функции состоит в том , что он может повторно использоваться через вне вашего проекта , чтобы проверить любой массив объектов , учитывая keyи valueпроверить.

Хватит говорить, посмотрим код

массив

const ceos = [
  {
    name: "Jeff Bezos",
    company: "Amazon"
  }, 
  {
    name: "Mark Zuckerberg",
    company: "Facebook"
  }, 
  {
    name: "Tim Cook",
    company: "Apple"
  }
];

функция

const arrayIncludesInObj = (arr, key, valueToCheck) => {
  let found = false;

  arr.some(value => {
    if (value[key] === valueToCheck) {
      found = true;
      return true; // this will break the loop once found
    }
  });

  return found;
}

Вызов / Usage

const found = arrayIncludesInObj(ceos, "name", "Tim Cook"); // true

const found = arrayIncludesInObj(ceos, "name", "Tim Bezos"); // false
Rotimi лучший
источник
-4

Я предпочел бы пойти с регулярным выражением.

Если ваш код выглядит следующим образом,

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    }
];

я бы порекомендовал

/"Name":"Magenic"/.test(JSON.stringify(vendors))
Сангвук Ким
источник
24
Некоторые люди, сталкиваясь с проблемой, думают: «Я знаю, я буду использовать регулярные выражения». Теперь у них две проблемы.
Craicerjack
Подать это под, только потому, что вы можете сделать что-то, не означает, что вы должны.
Лиам