Як визначити, чи містить масив Javascript об’єкт з атрибутом, який дорівнює заданому значенню?


658

У мене схожий масив

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

Як перевірити цей масив, щоб побачити, чи існує Magene? Я не хочу циклічно, якщо мені не доведеться. Я працюю з потенційно парою тисяч записів.


ОНОВЛЕНО

Оскільки це була популярна публікація, я думав, що поділюсь новим, що знайшов. І, схоже, @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
Домовились, не бачили цього раніше!
Девід Лоззі

Ви можете ще більше спростити це за допомогою функцій стрілок. Всі сучасні браузери підтримують це і виглядає приємніше.
Пьотр Кула

ви можете використовувати функцію карти, дуже корисну
Monir alhussini

Відповіді:


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 потрібно оголосити перед курсом для твердження.
Алекс

5
Ці параметри, здається, працюють зараз: vendors.forEach, vendors.filter, vendors.reduce
David Lozzi

1
що про JSON.stringify (постачальники) .indexOf ('Magenic')! == -1
Останній подих

1
@LastBreath, що може спричинити помилковий позитив досить легко, якщо 'Magenic'десь ще є об’єкт
Alex Turpin

947

Не потрібно винаходити колесоцикл, принаймні не явно (використовуючи функції стрілок , лише сучасні браузери ):

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 */
}

EDIT: Якщо вам потрібна сумісність із паршивими браузерами, тоді найкраща ставка:

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 (що не є паршивим браузером).
Ракета Hazmat

27
Думаю, це залежить від вашого визначення паршивого. Цей синтаксис є частиною javascript 1.8.
CAFxX

7
У вираженні укупорочних ви використовуєте тут в першому і друге прикладах мають нестандартний не використовувати! попередження від Mozilla (див. це посилання). Вони працювали лише у Firefox, а тепер застарілі і будуть видалені на користь функцій стрілок .
doppelgreener

2
@ 7hibault, оскільки someможе виявитись коротке замикання, як тільки name === "Magenic"знайдений об'єкт . З filter, він перевірятиме кожен елемент до кінця масиву та створює новий елемент масиву, який відповідає умові, а потім перевіряєlength
adiga

93

Не потрібно петлі. Три методи, які приходять в голову:

Array.prototype.some ()

Це найточніша відповідь на ваше запитання, тобто "перевірити, чи щось існує", що означає результат булінгу. Це буде правдою, якщо є якісь "магічні" об'єкти, інакше помилково:

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

Array.prototype.filter ()

Це поверне масив усіх об'єктів 'Magene', навіть якщо є лише один (поверне одноелементний масив):

let magenicVendors = vendors.filter( vendor => vendor['Name'] === '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 () повертає значення першого елемента в масиві, який задовольняє наданій функції тестування. В іншому випадку невизначене повертається.

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') буде самим елементом масиву (в даному випадку цілим об'єктом елемента). Якщо ви очікували, що результат змінної даних буде "істинним" або "хибним" замість хибного значення, ви були б сильно розчаровані.
adamgede

Дякую! Моє завдання було трохи іншим. Отримайте індекс Об'єкту в масиві => push if <0 || splice(index, 1)ось мій трохи оновлений код:const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id))
Леонід Задорожних

29

Ось як я це зробив

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
У випадку, якщо він все-таки захотів зберегти структуру об'єкта, використовувати його ще де
Keith.Abramo

21

Відповідно до специфікації ECMAScript 6, ви можете використовувати findIndex.

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

magenicIndexбуде утримувати або 0(що є індексом у масиві), або -1якщо його не було знайдено.


Просто для того, щоб зрозуміти людям, що 0 все одно збігатиметься як хибний результат, якби це було використано як умову. З цієї причини я думаю, що знайти () краще, оскільки ви отримаєте більш логічну правдиву оцінку.
dhj

15

Оскільки ОП задало питання, чи існує ключ чи ні .

Більш елегантне рішення, яке поверне булеве використання за допомогою функції зменшення ES6

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

Примітка: Початковий параметр redu є a, falseі якщо в масиві є ключ, він поверне true.

Сподіваємось, це допомагає для кращої та чіткішої реалізації коду


1
З якого часу !! [] дорівнює хибному?
Сергій

1
Гарний улов. Оновлена ​​відповідь за допомогою зменшення :)
Jay Chakra

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: Приймає функцію зворотного виклику, яка є тестом, масив потім повторюється із зворотним викликом, і якщо будь-який елемент проходить тест, повертається булева істина. Інакше помилкове повернення

Особливості найкраще пояснити на прикладі:

Приклад:

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
Примітка: повернення сюди не використовуйте
Едісон

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 занадто важка для вашої програми, вважайте, що не потрібна функція не використовується.

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

Тепер цей брудний Obj ви можете використовувати знову і знову без жодного циклу.

if(dirtyObj[vendor.Name]){
  console.log("Hey! I am available.");
}

1

Щоб порівняти один об'єкт з іншим, я поєдную цикл for in (використовується для циклу через об'єкти) та 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);

Альтернативний спосіб порівняння одного об’єкта з іншим - це використання вкладеного циклу з довжиною Object.keys ()., Щоб отримати кількість об'єктів у масиві. Код нижче:

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

Щоб відповісти на ваше точне запитання, якщо ви просто шукаєте значення в об'єкті, ви можете скористатись синглом для в циклі.

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

Виклик / використання

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

23
Деякі люди, стикаючись з проблемою, думають, "я знаю, я буду використовувати регулярні вирази". Зараз у них дві проблеми.
Craicerjack

Подайте це під, лише тому, що ви можете щось зробити, не означає, що вам слід.
Ліам
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.