Как определить, содержит ли массив 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 */
}

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: Нет причин использовать свободное равенство (==) вместо строгого равенства (===) при проверке имени.


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.


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)

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


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

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.");
}

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));

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) {
   ...
}

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

-4

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

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

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

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

/"Name":"Magenic"/.test(JSON.stringify(vendors))

24
Некоторые люди, сталкиваясь с проблемой, думают: «Я знаю, я буду использовать регулярные выражения». Теперь у них две проблемы.
Craicerjack

Подать это под, только потому, что вы можете сделать что-то, не означает, что вы должны.
Лиам
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.