¿Cómo determinar si la matriz Javascript contiene un objeto con un atributo que equivale a un valor dado?

658

Tengo una serie como

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

¿Cómo verifico esta matriz para ver si existe Magenic? No quiero repetir, a menos que tenga que hacerlo. Estoy trabajando con potencialmente un par de miles de registros.


ACTUALIZADO

Como esta ha sido una publicación popular, pensé en compartir algo nuevo que encontré. ¡Y parece que @CAFxX ya ha compartido esto! Debería leer esto más a menudo. Me encontré con https://benfrain.com/understanding-native-javascript-array-methods/ .

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

Y con ECMAScript 2015 es aún más simple usar las nuevas funciones de flecha:

vendors.filter(vendor => vendor.Name === "Magenic")
David Lozzi
fuente
Perdone el comentario aparentemente aleatorio, pero ¿su pregunta se refería a JSON o solo a matrices de JavaScript?
Alex Turpin
44
La solución @CAFxX es mejor, sería increíble si actualiza la solución seleccionada.
eMarine
1
De acuerdo, no vi eso antes!
David Lozzi
Puede simplificar esto ahora aún más utilizando las funciones de flecha. Todos los navegadores modernos lo admiten y se ve mejor.
Piotr Kula
puede usar la función de mapa, muy útil
Monir alhussini

Respuestas:

264

Edición de 2018 : esta respuesta es de 2011, antes de que los navegadores admitieran ampliamente los métodos de filtrado de matriz y las funciones de flecha. Echa un vistazo a la respuesta de CAFxX .

No hay una forma "mágica" de buscar algo en una matriz sin un bucle. Incluso si usa alguna función, la función misma usará un bucle. Lo que puede hacer es salir del ciclo tan pronto como encuentre lo que está buscando para minimizar el tiempo computacional.

var found = false;
for(var i = 0; i < vendors.length; i++) {
    if (vendors[i].Name == 'Magenic') {
        found = true;
        break;
    }
}
Alex Turpin
fuente
44
No hay problema. Tenga en cuenta que la solución de Keith también es muy viable y le ahorra bucles.
Alex Turpin
2
No necesita una marca si todo lo que necesita saber es si hay "algo" o no, simplemente puede verificar el valor del índice de escaneo con el tamaño de la matriz. Para que esto funcione, el índice var debe declararse antes de la declaración for, por supuesto.
Alex
55
Estas opciones parecen funcionar ahora: vendors.forEach, vendors.filter, vendors.reduce
David Lozzi
1
¿Qué pasa con JSON.stringify (vendedores) .indexOf ('Magenic')! == -1
Último aliento
2
@LastBreath que podría resultar en un falso positivo con bastante facilidad si 'Magenic'está en otro lugar del objeto
Alex Turpin
950

No es necesario reinventar el ruedabucle, al menos no explícitamente (usando funciones de flecha , solo navegadores modernos ):

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

o, mejor aún :

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

EDITAR: si necesita compatibilidad con navegadores malos, entonces su mejor opción es:

if (vendors.filter(function(e) { return e.Name === 'Magenic'; }).length > 0) {
  /* vendors contains the element we're looking for */
}
CAFxX
fuente
44
@Rocket, ¿por qué editaste mi respuesta? La sintaxis sin las llaves es javascript perfectamente válido .
CAFxX
44
La sintaxis "lambda" todavía no funciona en Chrome 16 (que no es un navegador pésimo).
Rocket Hazmat el
27
Depende de tu definición de pésimo, supongo. Esa sintaxis es parte de javascript 1.8.
CAFxX
77
Los cierres de expresiones que está usando aquí en el primer y segundo ejemplo tienen un No estándar, ¡no lo use! advertencia de Mozilla (ver ese enlace). Solo funcionaron en Firefox, y ahora están en desuso y se eliminarán en favor de las funciones de flecha .
doppelgreener
2
@ 7hibault porque somepuede cortocircuitar una vez que se encuentra un objeto con name === "Magenic". Con filter, verificará cada elemento hasta el final de la matriz y creará una nueva matriz de elementos que coincida con la condición, luego verificará ellength
adiga
93

No es necesario un bucle. Tres métodos que vienen a la mente:

Array.prototype.some ()

Esta es la respuesta más exacta para su pregunta, es decir, "verifique si algo existe", lo que implica un resultado bool. Esto será cierto si hay algún objeto 'Magenic', falso de lo contrario:

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

Array.prototype.filter ()

Esto devolverá una matriz de todos los objetos 'Magenic', incluso si solo hay uno (devolverá una matriz de un elemento):

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

Si intenta forzar esto a un valor booleano, no funcionará, ya que una matriz vacía (sin objetos 'Magenic') sigue siendo verdadera. Así que solo úsalo magenicVendors.lengthen tu condicional.

Array.prototype.find ()

Esto devolverá el primer objeto 'Magenic' (o undefinedsi no hay ninguno):

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

Esto coacciona a un booleano aceptable (cualquier objeto es verdadero, undefinedes falso).


Nota: Estoy usando vendor ["Name"] en lugar de vendor.Name debido a la extraña carcasa de los nombres de propiedad.

Nota 2: No hay razón para usar la igualdad suelta (==) en lugar de la igualdad estricta (===) al verificar el nombre.

boxtrain
fuente
55
Es útil señalar que debajo del capó, todos estos están en bucle. Estos también son más lentos desde el punto de vista computacional que simplemente para realizar bucles y realizar operaciones.
ThePartyTurtle
También podría compartir ese amor aquí: stackoverflow.com/questions/21748670/… para que más personas como yo no naveguen a esa página anterior y hagan suposiciones.
ThePartyTurtle
43

La respuesta aceptada todavía funciona, pero ahora tenemos un método nativo ECMAScript 6 [Array.find][1]para lograr el mismo efecto.

Citando a MDN:

El método find () devuelve el valor del primer elemento en la matriz que satisface la función de prueba proporcionada. De lo contrario, se devuelve indefinido.

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
}

Ver mi enlace jsfiddle Hay un polyfill para IE proporcionado por mozilla

TeaCoder
fuente
2
Podría ser más corto si lo hace return ele.id == '2', pero +1 para una buena solución ES6.
Lye Fish
Es bueno tener una nueva respuesta :) Me pregunto si el rendimiento es mejor o no que las respuestas anteriores ...
Emidomenge
Creo que es importante señalar que el valor de retorno de 'datos' (cuando ele.id coincide con una identificación, como '21') será el elemento de la matriz en sí (en este caso, el objeto de elemento completo). Si la expectativa fuera que el resultado de la variable de datos sería 'verdadero' o 'falso' en lugar de un valor falso, estaría muy decepcionado.
adamgede
¡Gracias! Mi tarea fue un poco diferente. Obtenga el índice de Object en la matriz => push if <0 || splice(index, 1)aquí está mi código un poco actualizado:const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id))
Leonid Zadorozhnykh
30

Así es como lo haría

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

array.some()El método comprueba si hay al menos un valor en una matriz que coincida con los criterios y devuelve un valor booleano. De aquí en adelante puedes ir con:

if (found) {
// do something
} else {
// do something else
}
Mirza Leka
fuente
22

A menos que quieras reestructurarlo así:

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

a lo que puedes hacer if(vendors.Magnetic)

Tendrás que hacer un bucle

Keith.Abramo
fuente
2
En caso de que todavía quisiera mantener la estructura del objeto para usarlo en otro lugar donde
Keith.Abramo
21

Según la especificación ECMAScript 6, puede usar findIndex.

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

magenicIndexretendrá 0(que es el índice en la matriz) o -1si no se encontró.

Lucas Bento
fuente
Solo para que las personas sepan que 0 aún coincidiría como resultado falso si se usara como condición. Por esta razón, creo que find () es mejor a medida que obtienes una evaluación de verdad más lógica .
dhj
15

Como el OP ha hecho la pregunta si la clave existe o no .

Una solución más elegante que devolverá un valor booleano utilizando la función de reducción ES6 puede ser

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

Nota: El parámetro inicial de reducir es a falsey si la matriz tiene la clave, devolverá verdadero.

Espero que ayude a una implementación de código mejor y más limpia

Jay Chakra
fuente
1
¿Desde cuándo? [] Es igual a falso?
Sergey
1
Buena atrapada. Respuesta actualizada usando reduce :)
Jay Chakra
1
Esto está mal. El primer parámetro para reducees el acumulador y no el vendorobjeto. Esto verifica false.Name === "Magenic"en cada bucle y devuelve falso
adiga
@adiga: corregido.
Jay Chakra
1
Por favor, compruebe también la solución de Mirza Leka. Una solución mucho más elegante.
Jay Chakra
13

No puedes sin mirar el objeto realmente.

Probablemente deberías cambiar un poco tu estructura, como

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

Entonces puedes usarlo como un hash de búsqueda.

vendors['Microsoft']; // 'DEF'
vendors['Apple']; // undefined
jAndy
fuente
6

Puede ser demasiado tarde, pero la matriz de JavaScript tiene dos métodos somey un everymétodo que devuelve un valor booleano y puede ayudarlo a lograrlo.

Creo que somesería más apropiado para lo que pretendes lograr.

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

Algunos validan que cualquiera de los objetos en la matriz satisface la condición dada.

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

Cada valida que todos los objetos en la matriz satisfacen la condición dada.

Akinjiola Toni
fuente
No funciona const array1 = [{name:'Mike'},{name:'Alice'}]; console.log(array1.every(item => item.name !== 'Mike'));, debería volver cierto
Thanwa Ch.
Lo siento amigo, quise decir some, actualizará mi respuesta.
Akinjiola Toni
5

Tienes que hacer un bucle, no hay forma de evitarlo.

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

Por supuesto, podría usar una biblioteca como linq.js para hacer esto más agradable:

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

(ver jsFiddle para una demostración)

Dudo que linq.js sea más rápido que un ciclo directo, pero ciertamente es más flexible cuando las cosas se ponen un poco más complicadas.

Tomalak
fuente
5

Prueba de elementos de matriz:

JS ofrece funciones de matriz que le permiten lograr esto con relativa facilidad. Son los siguientes:

  1. Array.prototype.filter: Toma una función de devolución de llamada que es una prueba, la matriz se repite con la devolución de llamada y se filtra de acuerdo con esta devolución de llamada. Se devuelve una nueva matriz filtrada.
  2. Array.prototype.some: Toma una función de devolución de llamada que es una prueba, la matriz se repite con su devolución de llamada y si algún elemento pasa la prueba, se devuelve el verdadero booleano. De lo contrario, se devuelve falso

Los detalles se explican mejor a través de un ejemplo:

Ejemplo:

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

Soporte de navegador:

Estas 2 funciones son ES6funciones, no todos los navegadores pueden admitirlas. Para superar esto, puede usar un polyfill. Aquí está el polyfill para Array.prototype.some(de 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;
  };
}

Willem van der Veen
fuente
4

si está utilizando jquery, puede aprovechar grep para crear una matriz con todos los objetos coincidentes:

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

y luego use la matriz de resultados:

for (var i=0, l=results.length; i<l; i++) {
    console.log(results[i].ID);
}
Eitan
fuente
3

Corrígeme si me equivoco ... podría haber usado un forEachmétodo como este,

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

   }
});

Hoy en día estoy acostumbrado, por su simplicidad y su palabra que se explica por sí misma. Gracias.

Siddhesh Mishra
fuente
1
Nota: no hay uso de retorno aquí
Edison
2

Puedes probar esto, es trabajo para mí.

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 } ]
jenish
fuente
Bueno, esta es una pregunta muy antigua y creo que su actualización ya tiene la mejor solución hoy en día.
Federico Galfione
1

Puedes usar lodash . Si la biblioteca lodash es demasiado pesada para su aplicación, considere fragmentar la función innecesaria que no se utiliza.

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

Esta es solo una forma de hacer esto. Otro puede ser:

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

console.log(arr);

El ejemplo anterior también se puede reescribir sin usar ninguna biblioteca como:

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

Espero que mi respuesta ayude.

Abhay Shiro
fuente
1

Muchas respuestas aquí son buenas y bastante fáciles. Pero si su conjunto de objetos tiene un conjunto fijo de valores, puede usar el siguiente truco:

Mapear todo el nombre en un objeto.

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

Ahora este dirtyObj puede usar una y otra vez sin ningún bucle.

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

Para comparar un objeto con otro, combino un bucle for in (usado para recorrer objetos) y some (). No tiene que preocuparse de que una matriz se salga de los límites, etc., de modo que ahorre algo de código. La documentación sobre .some se puede encontrar aquí

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

Una forma alternativa de comparar un objeto con otro es usar un bucle for anidado con Object.keys (). Length para obtener la cantidad de objetos en la matriz. Código a continuación:

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

Para responder a su pregunta exacta, si solo está buscando un valor en un objeto, puede usar un solo ciclo in.

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');
    }
}
Lentejuela
fuente
0

Alternativamente, puedes hacer:

const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));
BrunoWest
fuente
1
será mejor que digas por qué puede hacer eso
Azzabi Haythem
0

var without2 = (arr, args) => arr.filter(v => v.id !== args.id); Ejemplo:

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

Resultado: sin2 ([{id: 1}, {id: 1}, {id: 2}], {id: 2})

behzad abbasi
fuente
Creo que querías decir Resultado: [{id: 1}, {id: 1}]
Isaac Pak el
0
const a = [{one:2},{two:2},{two:4}]
const b = a.filter(val => "two" in val).length;
if (b) {
   ...
}
usuario1665355
fuente
3
Por favor, una descripción y asegúrese de que el ejemplo que proporcione funcione ... (el filtro no cambiará la matriz original sino que la clonará).
Moshe Simantov
-1

Mi enfoque para resolver este problema es usar ES6 y crear una función que haga la comprobación por nosotros. El beneficio de esta función es que puede ser reutilizable a lo largo de su proyecto para verificar cualquier conjunto de objetos dado el keyy valuepara verificar.

HABLAR SUFICIENTE, VAMOS EL CÓDIGO

Formación

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

Función

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

Llamada / Uso

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

const found = arrayIncludesInObj(ceos, "name", "Tim Bezos"); // false
rotimi-best
fuente
-4

Prefiero ir con regex.

Si su código es el siguiente,

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

yo recomendaria

/"Name":"Magenic"/.test(JSON.stringify(vendors))
sangwook kim
fuente
24
Algunas personas, cuando se enfrentan a un problema, piensan "Lo sé, usaré expresiones regulares". Ahora ellos tienen dos problemas.
Craicerjack
Archivar esto debajo, solo porque puedes hacer algo, no significa que debas hacerlo.
Liam