objects - recorrer array de objetos javascript
¿Cómo verifico si una matriz incluye un objeto en JavaScript? (30)
¿Cuál es la forma más concisa y eficiente de averiguar si una matriz de JavaScript contiene un objeto?
Esta es la única manera que conozco para hacerlo:
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
¿Hay una manera mejor y más concisa para lograr esto?
Esto está muy relacionado con la pregunta de desbordamiento de pila ¿La mejor forma de encontrar un elemento en una matriz de JavaScript? que aborda la búsqueda de objetos en una matriz utilizando indexOf
.
Aquí hay una implementación compatible con JavaScript 1.6 de Array.indexOf
:
if (!Array.indexOf)
{
Array.indexOf = [].indexOf ?
function (arr, obj, from) { return arr.indexOf(obj, from); }:
function (arr, obj, from) { // (for IE6)
var l = arr.length,
i = from ? parseInt( (1*from) + (from<0 ? l:0), 10) : 0;
i = i<0 ? 0 : i;
for (; i<l; i++) {
if (i in arr && arr[i] === obj) { return i; }
}
return -1;
};
}
Así es como lo hace Prototype :
/**
* Array#indexOf(item[, offset = 0]) -> Number
* - item (?): A value that may or may not be in the array.
* - offset (Number): The number of initial items to skip before beginning the
* search.
*
* Returns the position of the first occurrence of `item` within the array — or
* `-1` if `item` doesn''t exist in the array.
**/
function indexOf(item, i) {
i || (i = 0);
var length = this.length;
if (i < 0) i = length + i;
for (; i < length; i++)
if (this[i] === item) return i;
return -1;
}
También vea here cómo se enganchan.
Como han mencionado otros, puedes usar Array.indexOf
, pero no está disponible en todos los navegadores. Aquí está el código de https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf para hacer que funcione de la misma manera en navegadores más antiguos.
indexOf es una adición reciente al estándar ECMA-262; como tal, puede no estar presente en todos los navegadores. Puede solucionar esto insertando el siguiente código al principio de sus scripts, permitiendo el uso de indexOf en implementaciones que no lo admiten de forma nativa. Este algoritmo es exactamente el especificado en ECMA-262, quinta edición, asumiendo que Object, TypeError, Number, Math.floor, Math.abs y Math.max tienen su valor original.
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
"use strict";
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n != n) { // shortcut for verifying if it''s NaN
n = 0;
} else if (n != 0 && n != Infinity && n != -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
}
}
De acuerdo, ¡solo puede optimizar su código para obtener el resultado! Hay muchas maneras de hacer esto que son más limpias y mejores, pero solo quería obtener su patrón y aplicar a eso usando JSON.stringify
, simplemente haga algo como esto en su caso:
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (JSON.stringify(a[i]) === JSON.stringify(obj)) {
return true;
}
}
return false;
}
ECMAScript 6 tiene una propuesta elegante en encontrar.
El método de búsqueda ejecuta la función de devolución de llamada una vez para cada elemento presente en la matriz hasta que encuentra uno donde la devolución de llamada devuelve un valor verdadero. Si se encuentra un elemento de este tipo, find devuelve inmediatamente el valor de ese elemento. De lo contrario, encontrar devuelve indefinido. la devolución de llamada se invoca solo para los índices de la matriz que tienen valores asignados; no se invoca para los índices que se han eliminado o que nunca se han asignado valores.
Aquí está la documentación de MDN sobre eso.
La funcionalidad de búsqueda funciona así.
function isPrime(element, index, array) {
var start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) return false;
}
return (element > 1);
}
console.log( [4, 6, 8, 12].find(isPrime) ); // Undefined, not found
console.log( [4, 5, 8, 12].find(isPrime) ); // 5
Puede usar esto en ECMAScript 5 y más abajo definiendo la función .
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, ''find'', {
enumerable: false,
configurable: true,
writable: true,
value: function(predicate) {
if (this == null) {
throw new TypeError(''Array.prototype.find called on null or undefined'');
}
if (typeof predicate !== ''function'') {
throw new TypeError(''predicate must be a function'');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
if (i in list) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
}
return undefined;
}
});
}
ECMAScript 7 introduce array.includes(value) .
Se puede usar así:
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
También acepta un segundo argumento opcional de fromIndex
:
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
A diferencia de indexOf
, que utiliza la comparación de igualdad estricta , includes
comparaciones con el algoritmo de igualdad SameValueZero . Eso significa que puede detectar si una matriz incluye un NaN
:
[1, 2, NaN].includes(NaN); // true
Además, a diferencia de indexOf
, includes
no omite los índices que faltan:
new Array(5).includes(undefined); // true
Actualmente todavía es un borrador, pero se puede polyfill con polyfill para que funcione en todos los navegadores.
Extender el objeto de la Array
JavaScript es una muy mala idea porque introduce nuevas propiedades (sus métodos personalizados) for-in
bucles for-in
que pueden romper los scripts existentes. Hace unos años, los autores de la biblioteca Prototype tuvieron que rediseñar la implementación de su biblioteca para eliminar este tipo de cosas.
Si no necesita preocuparse por la compatibilidad con otro JavaScript que se ejecuta en su página, hágalo, de lo contrario, le recomendaría la solución de función independiente más incómoda pero más segura.
Las respuestas principales asumen tipos primitivos, pero si desea averiguar si una matriz contiene un objeto con algún rasgo, Array.prototype.some() es una solución muy elegante:
const items = [ {a: ''1''}, {a: ''2''}, {a: ''3''} ]
items.some(item => item.a === ''3'') // returns true
items.some(item => item.a === ''4'') // returns false
Lo bueno de esto es que la iteración se cancela una vez que se encuentra el elemento, por lo que se guardan ciclos de iteración innecesarios.
Además, encaja muy bien en una sentencia if
, ya que devuelve un booleano:
if (items.some(item => item.a === ''3'')) {
// do something
}
* Como señala Jamess en el comentario, a partir de hoy, septiembre de 2018, Array.prototype.some()
es totalmente compatible: tabla de soporte de caniuse.com
Los navegadores actuales tienen Array#includes
, que hace exactamente eso, es ampliamente compatible y tiene un polyfill para navegadores más antiguos.
> [''joe'', ''jane'', ''mary''].includes(''jane'');
true
También puede usar Array#indexOf
, que es menos directo, pero no requiere Polyfills para navegadores desactualizados.
jQuery ofrece $.inArray
, que es funcionalmente equivalente a Array#indexOf
.
underscore.js , una biblioteca de utilidades de JavaScript, ofrece _.contains(list, value)
, alias _.include(list, value)
, los cuales usan indexOf internamente si se pasa una matriz de JavaScript.
Algunos otros marcos ofrecen métodos similares:
- Kit de herramientas Dojo:
dojo.indexOf(array, value, [fromIndex, findLast])
- Prototipo:
array.indexOf(value)
- MooTools:
array.indexOf(value)
-
findValue(array, value)
:findValue(array, value)
- MS Ajax:
array.indexOf(value)
- Ext:
Ext.Array.contains(array, value)
- Lodash:
_.includes(array, value, [from])
(es_.contains
anterior 4.0.0) - ECMAScript 2016:
array.includes(value)
Observe que algunos marcos implementan esto como una función, mientras que otros agregan la función al prototipo de matriz.
Pensando fuera de la caja por un segundo, si está haciendo esta llamada muchas veces, es mucho más eficiente usar una matriz asociativa que un Mapa para hacer búsquedas usando una función hash.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
Si bien array.indexOf(x)!=-1
es la forma más concisa de hacerlo (y ha sido compatible con navegadores que no son de Internet Explorer durante más de una década ...), no es O (1), sino O (1) N), que es terrible. Si su matriz no cambiará, puede convertirla a una tabla hash, luego haga la table[x]!==undefined
o ===undefined
:
Array.prototype.toTable = function() {
var t = {};
this.forEach(function(x){t[x]=true});
return t;
}
Manifestación:
var toRemove = [2,4].toTable();
[1,2,3,4,5].filter(function(x){return toRemove[x]===undefined})
(Desafortunadamente, aunque puede crear un Array.prototype.contains para "congelar" una matriz y almacenar una tabla hash en this._cache en dos líneas, esto daría resultados incorrectos si eligiera editar su matriz más adelante. JavaScript no tiene suficientes enlaces para te permite mantener este estado, a diferencia de Python, por ejemplo.)
Si está comprobando repetidamente la existencia de un objeto en una matriz, tal vez debería examinar
- Mantener la matriz ordenada en todo momento haciendo la ordenación por inserción en su matriz (coloque los nuevos objetos en el lugar correcto)
- Realice la actualización de los objetos como eliminar + ordenar la operación de inserción y
- Utilice una búsqueda de búsqueda binaria en sus
contains(a, obj)
.
Si está utilizando JavaScript 1.6 o posterior (Firefox 1.5 o posterior) puede usar Array.indexOf . De lo contrario, creo que vas a terminar con algo similar a tu código original.
Solución que funciona en todos los navegadores modernos:
function contains(arr, obj) {
const stringifiedObj = JSON.stringify(obj); // Cache our object to not call `JSON.stringify` on every iteration
return arr.some(item => JSON.stringify(item) === stringifiedObj);
}
Uso:
contains([{a: 1}, {a: 2}], {a: 1}); // true
Solución IE6 +:
function contains(arr, obj) {
var stringifiedObj = JSON.stringify(obj)
return arr.some(function (item) {
return JSON.stringify(item) === stringifiedObj;
});
}
// .some polyfill, not needed for IE9+
if (!(''some'' in Array.prototype)) {
Array.prototype.some = function (tester, that /*opt*/) {
for (var i = 0, n = this.length; i < n; i++) {
if (i in this && tester.call(that, this[i], i, this)) return true;
} return false;
};
}
Uso:
contains([{a: 1}, {a: 2}], {a: 1}); // true
¿Por qué usar JSON.stringify
?
Array.indexOf
y Array.includes
(así como la mayoría de las respuestas aquí) solo se comparan por referencia y no por valor.
[{a: 1}, {a: 2}].includes({a: 1});
// false, because {a: 1} is a new object
Prima
ES6 no optimizado de una sola línea:
[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));
// true
Nota: la comparación de objetos por valor funcionará mejor si las claves están en el mismo orden, por lo que, para estar seguro, puede ordenarlas primero con un paquete como este: https://www.npmjs.com/package/sort-keys
Actualización de la función contains
con una optimización de perf. Gracias itinance por señalarlo.
También puedes usar este truco:
var arrayContains = function(object) {
return (serverList.filter(function(currentObject) {
if (currentObject === object) {
return currentObject
}
else {
return false;
}
}).length > 0) ? true : false
}
Un trazador de líneas:
function contains(arr, x) {
return arr.filter(function(elem) { return elem == x }).length > 0;
}
Uno puede usar el Set que tiene el método "has ()":
function contains(arr, obj) {
var proxy = new Set(arr);
if (proxy.has(obj))
return true;
else
return false;
}
var arr = [''Happy'', ''New'', ''Year''];
console.log(contains(arr, ''Happy''));
Usa la función de lodash.
Es conciso, preciso y tiene un gran soporte multiplataforma.
La respuesta aceptada ni siquiera cumple con los requisitos.
Requisitos: Recomiende la forma más concisa y eficiente de averiguar si una matriz de JavaScript contiene un objeto.
Respuesta aceptada:
$.inArray({''b'': 2}, [{''a'': 1}, {''b'': 2}])
> -1
Mi recomendación:
_.some([{''a'': 1}, {''b'': 2}], {''b'': 2})
> true
Notas:
$ .inArray funciona bien para determinar si existe un valor escalar en una matriz de escalares ...
$.inArray(2, [1,2])
> 1
... pero la pregunta claramente pide una manera eficiente de determinar si un objeto está contenido en una matriz.
Para manejar tanto los escalares como los objetos, puedes hacer esto:
(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)
Usamos este fragmento de código (funciona con objetos, matrices, cadenas):
/*
* @function
* @name Object.prototype.inArray
* @description Extend Object prototype within inArray function
*
* @param {mix} needle - Search-able needle
* @param {bool} searchInKey - Search needle in keys?
*
*/
Object.defineProperty(Object.prototype, ''inArray'',{
value: function(needle, searchInKey){
var object = this;
if( Object.prototype.toString.call(needle) === ''[object Object]'' ||
Object.prototype.toString.call(needle) === ''[object Array]''){
needle = JSON.stringify(needle);
}
return Object.keys(object).some(function(key){
var value = object[key];
if( Object.prototype.toString.call(value) === ''[object Object]'' ||
Object.prototype.toString.call(value) === ''[object Array]''){
value = JSON.stringify(value);
}
if(searchInKey){
if(value === needle || key === needle){
return true;
}
}else{
if(value === needle){
return true;
}
}
});
},
writable: true,
configurable: true,
enumerable: false
});
Uso:
var a = {one: "first", two: "second", foo: {three: "third"}};
a.inArray("first"); //true
a.inArray("foo"); //false
a.inArray("foo", true); //true - search by keys
a.inArray({three: "third"}); //true
var b = ["one", "two", "three", "four", {foo: ''val''}];
b.inArray("one"); //true
b.inArray(''foo''); //false
b.inArray({foo: ''val''}) //true
b.inArray("{foo: ''val''}") //false
var c = "String";
c.inArray("S"); //true
c.inArray("s"); //false
c.inArray("2", true); //true
c.inArray("20", true); //false
Utilizar:
Array.prototype.contains = function(x){
var retVal = -1;
// x is a primitive type
if(["string","number"].indexOf(typeof x)>=0 ){ retVal = this.indexOf(x);}
// x is a function
else if(typeof x =="function") for(var ix in this){
if((this[ix]+"")==(x+"")) retVal = ix;
}
//x is an object...
else {
var sx=JSON.stringify(x);
for(var ix in this){
if(typeof this[ix] =="object" && JSON.stringify(this[ix])==sx) retVal = ix;
}
}
//Return False if -1 else number if numeric otherwise string
return (retVal === -1)?false : ( isNaN(+retVal) ? retVal : +retVal);
}
Sé que no es la mejor manera de hacerlo, pero como no existe una forma de interacción entre los objetos nativa e incompatible, supongo que esto es lo más parecido a comparar dos entidades en una matriz. Además, extender el objeto Array puede que no sea una buena idea, pero a veces está bien (si está al tanto de esto y de la compensación).
Utilizar:
function isInArray(array, search)
{
return array.indexOf(search) >= 0;
}
// Usage
if(isInArray(my_array, "my_value"))
{
//...
}
Utilizar:
var myArray = [''yellow'', ''orange'', ''red''] ;
alert(!!~myArray.indexOf(''red'')); //true
Para saber exactamente qué hace la tilde
en este momento, consulte esta pregunta ¿Qué hace una tilde cuando precede a una expresión? .
Yo uso lo siguiente:
Array.prototype.contains = function (v) {
return this.indexOf(v) > -1;
}
var a = [ ''foo'', ''bar'' ];
a.contains(''foo''); // true
a.contains(''fox''); // false
indexOf
quizás, pero es una "extensión de JavaScript al estándar ECMA-262; como tal, puede no estar presente en otras implementaciones del estándar".
Ejemplo:
[1, 2, 3].indexOf(1) => 0
["foo", "bar", "baz"].indexOf("bar") => 1
[1, 2, 3].indexOf(4) => -1
AFAICS Microsoft no ofrece algún tipo de alternativa a esto, pero puede agregar una funcionalidad similar a los arreglos en Internet Explorer (y otros navegadores que no admiten indexOf
) si lo desea, como lo revela una búsqueda rápida en Google (por ejemplo, esto uno ).
b
es el valor, y a
es la matriz. Devuelve true
o false
:
function(a, b) {
return a.indexOf(b) != -1
}
Actualización: como @orip menciona en los comentarios, el punto de referencia vinculado se realizó en 2008, por lo que los resultados pueden no ser relevantes para los navegadores modernos. Sin embargo, es probable que necesites esto para admitir navegadores no modernos de todos modos y probablemente no se hayan actualizado desde entonces. Siempre prueba por ti mismo.
Como han dicho otros, la iteración a través de la matriz es probablemente la mejor manera, pero se ha demostrado que un bucle de disminución es la forma más rápida de iterar en JavaScript. Así que es posible que desee volver a escribir su código de la siguiente manera:
function contains(a, obj) {
var i = a.length;
while (i--) {
if (a[i] === obj) {
return true;
}
}
return false;
}
Por supuesto, también puede extender el prototipo Array:
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
Y ahora puedes simplemente usar lo siguiente:
alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains(''2'')); // => false
Una alternativa a un lastIndexOf
bidireccional con la esperanza de que sea más rápida
2015
Si bien el nuevo método array.includes(value) es muy bueno, el soporte es básicamente cero por ahora.
Hace mucho tiempo que pensaba en la forma de reemplazar las funciones lentas indexOf / lastIndexOf.
Ya se ha encontrado una forma de desempeño, mirando las respuestas principales. De entre los que elegí, la función contains
publicada por @Damir Zekic, que debería ser la más rápida. Pero también afirma que los puntos de referencia son de 2008 y, por lo tanto, están desactualizados.
También prefiero pasar for
alto, pero no por una razón específica, terminé de escribir la función con un bucle for. También podría hacerse con un while --
.
Tenía curiosidad por saber si la iteración era mucho más lenta si revisaba ambos lados de la matriz mientras lo hacía. Aparentemente no, y esta función es aproximadamente dos veces más rápida que las más votadas. Obviamente también es más rápido que el nativo. Esto en un entorno del mundo real, donde nunca se sabe si el valor que está buscando está al principio o al final de la matriz.
Cuando sabe que acaba de presionar una matriz con un valor, el uso de lastIndexOf sigue siendo probablemente la mejor solución, pero si tiene que viajar a través de matrices grandes y el resultado podría estar en todas partes, esta podría ser una solución sólida para hacer las cosas más rápido.
Indice bidireccionalOf / lastIndexOf
function bidirectionalIndexOf(a, b, c, d, e){
for(c=a.length,d=c*1; c--; ){
if(a[c]==b) return c; //or this[c]===b
if(a[e=d-1-c]==b) return e; //or a[e=d-1-c]===b
}
return -1
}
//Usage
bidirectionalIndexOf(array,''value'');
Prueba de rendimiento
http://jsperf.com/bidirectionalindexof
Como prueba he creado una matriz con 100k entradas.
Tres consultas: al principio, en el medio y al final de la matriz.
Espero que también encuentres esto interesante y pruebes el rendimiento.
Nota: Como puede ver, modifiqué ligeramente la función de contenido para reflejar la salida de indexOf y lastIndexOf (básicamente básicamente con el index
y false
con -1
). Eso no debería dañarlo.
La variante prototipo de matriz.
Object.defineProperty(Array.prototype,''bidirectionalIndexOf'',{value:function(b,c,d,e){
for(c=this.length,d=c*1; c--; ){
if(this[c]==b) return c; //or this[c]===b
if(this[e=d-1-c] == b) return e; //or this[e=d-1-c]===b
}
return -1
},writable:false, enumerable:false});
// Usage
array.bidirectionalIndexOf(''value'');
La función también se puede modificar fácilmente para devolver verdadero o falso o incluso el objeto, la cadena o lo que sea.
Y aquí está la variante while
:
function bidirectionalIndexOf(a, b, c, d){
c=a.length; d=c-1;
while(c--){
if(b===a[c]) return c;
if(b===a[d-c]) return d-c;
}
return c
}
// Usage
bidirectionalIndexOf(array,''value'');
¿Cómo es esto posible?
Creo que el cálculo simple para obtener el índice reflejado en una matriz es tan simple que es dos veces más rápido que hacer una iteración de bucle real.
Este es un ejemplo complejo que realiza tres verificaciones por iteración, pero esto solo es posible con un cálculo más largo que causa la desaceleración del código.
De ninguna manera lo mejor, pero me estaba volviendo creativo y añadiendo al repertorio.
No uses esto
Object.defineProperty(Array.prototype, ''exists'', {
value: function(element, index) {
var index = index || 0
return index === this.length ? -1 : this[index] === element ? index : this.exists(element, ++index)
}
})
// Outputs 1
console.log([''one'', ''two''].exists(''two''));
// Outputs -1
console.log([''one'', ''two''].exists(''three''));
console.log([''one'', ''two'', ''three'', ''four''].exists(''four''));
function contains(a, obj) {
return a.some(function(element){return element == obj;})
}
Array.prototype.some() se agregó al estándar ECMA-262 en la quinta edición
function inArray(elem,array)
{
var len = array.length;
for(var i = 0 ; i < len;i++)
{
if(array[i] == elem){return i;}
}
return -1;
}
Devuelve el índice de matriz si se encuentra, o -1 si no se encuentra