Array.push () si no existe?

247

¿Cómo puedo ingresar a una matriz si ninguno de los valores existe? Aquí está mi matriz:

[
    { name: "tom", text: "tasty" },
    { name: "tom", text: "tasty" },
    { name: "tom", text: "tasty" },
    { name: "tom", text: "tasty" },
    { name: "tom", text: "tasty" }
]

Si tratara de empujar de nuevo en la matriz, ya sea con name: "tom"o text: "tasty", no quiero que le pase nada ... pero si ninguno de los que están allí entonces yo quiero que.push()

¿Cómo puedo hacer esto?

Tarnfeld
fuente
55
Use un diccionario (hash / tree) en lugar de una matriz.
Thomas Eding el
¿Están todos estos disponibles en javascript?
rahul
3
use un Set
Drax
1
El conjunto no funciona con una variedad de objetos
rffaguiar

Respuestas:

114

Puede ampliar el prototipo de matriz con un método personalizado:

// check if an element exists in array using a comparer function
// comparer : function(currentElement)
Array.prototype.inArray = function(comparer) { 
    for(var i=0; i < this.length; i++) { 
        if(comparer(this[i])) return true; 
    }
    return false; 
}; 

// adds an element to the array if it does not already exist using a comparer 
// function
Array.prototype.pushIfNotExist = function(element, comparer) { 
    if (!this.inArray(comparer)) {
        this.push(element);
    }
}; 

var array = [{ name: "tom", text: "tasty" }];
var element = { name: "tom", text: "tasty" };
array.pushIfNotExist(element, function(e) { 
    return e.name === element.name && e.text === element.text; 
});
Darin Dimitrov
fuente
55
Creo que su activista (¿comparador?) Debería tomar dos argumentos, esto simplificaría el caso cuando el valor agregado esté en línea y no en una variable a la que pueda acceder en su función. array.pushIfNotExist ({nombre: "tom", texto: "sabroso"}, función (a, b) {return a.name === b.name && a.text === b.text;});
Vincent Robert
26
Me pregunto por qué esto no es nativo del idioma, olvídate de cómo se implementa, la idea de 'agregar solo si es única' es tan fundamental que se supone que existe.
justSteve
99
Es mejor extender el prototipo de Array con el método JavaScript 1.6 IndexOf en lugar de su inArray.
Eugene Gluhotorenko 01 de
77
Array.findIndex()es una función JS incorporada que logrará lo mismo que su código.
44
Extender los objetos integrados directamente es una mala práctica.
Slava Fomin II
429

Para una matriz de cadenas (pero no una matriz de objetos), puede verificar si existe un elemento llamando .indexOf()y si no existe, simplemente inserte el elemento en la matriz:

var newItem = "NEW_ITEM_TO_ARRAY";
var array = ["OLD_ITEM_1", "OLD_ITEM_2"];

array.indexOf(newItem) === -1 ? array.push(newItem) : console.log("This item already exists");

console.log(array)

Jiří Zahálka
fuente
50
No estoy seguro de por qué esto no se marca como correcto. No utiliza elementos externos, no requiere crear una extensión y es muy simple. Respuesta perfecta para la pregunta de operaciones.
pimbrouwers
39
En la pregunta inicial, los valores de la matriz son objetos, no cadenas (y esta solución no funciona como si los valores fueran objetos).
Simon Hola,
12
@EmilPedersen, en realidad no. Intenta if (a.indexOf({ name: "tom", text: "tasty" })!=-1) a.push({ name: "tom", text: "tasty" })dos veces. Agregará un objeto 'similar' dos veces.
commonpike
18
Esta respuesta debe eliminarse ya que es objetivamente incorrecta, pero aún así atrajo el mayor número de votos a favor.
nikola
2
Esta no es una respuesta correcta, ¿por qué se acepta? Simplemente funciona para matrices Js, no para objetos dentro de matrices.
digitai
100

Es bastante fácil hacerlo usando la Array.findIndexfunción, que toma una función como argumento:

var a = [{name:"bull", text: "sour"},
    { name: "tom", text: "tasty" },
    { name: "tom", text: "tasty" }
]
var index = a.findIndex(x => x.name=="bob")
// here you can check specific property for an object whether it exist in your array or not

if (index === -1){
    a.push({your_object});
}
else console.log("object already exists")
Ashish Yadav
fuente
8
Esta es la respuesta chicos !!
Jafed
2
más relevante para agregar un elemento en la matriz si no está presente
akshay bagade
1
Gracias, estaba buscando esto en todas partes.
dt192
41

http://api.jquery.com/jQuery.unique/

var cleanArray = $.unique(clutteredArray);

usted también podría estar interesado en makeArray

El ejemplo anterior es mejor al decir que verifique si existe antes de presionar. Veo en retrospectiva que también dice que puedes declararlo como parte del prototipo (supongo que también es Class Extension), por lo que no hay una gran mejora a continuación.

Excepto que no estoy seguro de si indexOf es una ruta más rápida que inArray. probablemente.

Array.prototype.pushUnique = function (item){
    if(this.indexOf(item) == -1) {
    //if(jQuery.inArray(item, this) == -1) {
        this.push(item);
        return true;
    }
    return false;
}
MistereeDevlord
fuente
22
Desde el enlace jQuery: Note that this only works on arrays of DOM elements, not strings or numbers.Además, indexOf no funciona en IE8 :(
dmathisen
Puede usar lodash _.indexOf, que funcionará en IE8
LTroya el
25

Utilice una biblioteca js como underscore.js por estos motivos exactamente. Uso: union: Calcula la unión de las matrices pasadas: la lista de elementos únicos, en orden, que están presentes en una o más de las matrices.

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]
Ronen Rabinovici
fuente
8
Tenga en cuenta que esto devuelve una nueva matriz y en realidad no empuja a una matriz existente.
ilovett
44
En mi humilde opinión, realmente no hay necesidad de incorporar un marco para probar algo tan simple
DrewT
24

¿Me gusta esto?

var item = "Hello World";
var array = [];
if (array.indexOf(item) === -1) array.push(item);

Con objeto

var item = {name: "tom", text: "tasty"}
var array = [{}]
if (!array.find(o => o.name === 'tom' && o.text === 'tasty'))
    array.push(item)
Ronnie Royston
fuente
44
array.findes una mala idea porque busca en toda la matriz. Uso findIndex, que solo busca hasta la primera aparición.
3
@ K48 de acuerdo con esto: stackoverflow.com/a/33759573/5227365 "find" se detiene después de encontrar el artículo
Pascal
19

Sé que esta es una pregunta muy antigua, pero si estás usando ES6 puedes usar una versión muy pequeña:

[1,2,3].filter(f => f !== 3).concat([3])

Muy fácil, al principio agregue un filtro que elimine el elemento, si ya existe, y luego agréguelo a través de un concat.

Aquí hay un ejemplo más realista:

const myArray = ['hello', 'world']
const newArrayItem

myArray.filter(f => f !== newArrayItem).concat([newArrayItem])

Si su matriz contiene objetos, puede adaptar la función de filtro de esta manera:

someArray.filter(f => f.some(s => s.id === myId)).concat([{ id: myId }])
Michael J. Zoidl
fuente
3
Una solución bastante elegante aquí. ¡Gracias!
Jonathan Picazo
18

Empuje dinámicamente

var a = [
  {name:"bull", text: "sour"},
  {name: "tom", text: "tasty" },
  {name: "Jerry", text: "tasty" }
]

function addItem(item) {
  var index = a.findIndex(x => x.name == item.name)
  if (index === -1) {
    a.push(item);
  }else {
    console.log("object already exists")
  }
}

var item = {name:"bull", text: "sour"};
addItem(item);

En un método simple

var item = {name:"bull", text: "sour"};
a.findIndex(x => x.name == item.name) == -1 ? a.push(item) : console.log("object already exists")

Si la matriz contiene solo tipos primitivos / matriz simple

var b = [1, 7, 8, 4, 3];
var newItem = 6;
b.indexOf(newItem) === -1 && b.push(newItem);
Gopala raja naika
fuente
2
Salud para tus manos. Solución simple y hermosa @Gopala raja naika
Nasimba
11

Te sugiero que uses un Set ,

Los conjuntos solo permiten entradas únicas, lo que resuelve automáticamente su problema.

Los conjuntos se pueden declarar así:

const baz = new Set(["Foo","Bar"])
Michael McQuade
fuente
Gracias por señalar eso @Michael. Buena solución para cuando queremos mantener datos distintos con un mínimo esfuerzo. FWIW, es importante tener en cuenta que el rendimiento de la matriz es mejor ya que requiere menos CPU para recuperar el elemento cuando es necesario.
Ben
La pregunta se refiere Array.push, por lo que Set.addes el equivalente a eso.
bryc
9

Código fácil, si 'indexOf' devuelve '-1' significa que el elemento no está dentro de la matriz, entonces la condición '=== -1' recupera verdadero / falso.

El operador '&&' significa 'y', por lo que si la primera condición es verdadera, la enviamos a la matriz.

array.indexOf(newItem) === -1 && array.push(newItem);
Eric Valero
fuente
@ D. Lawrence Sí, mucho mejor ahora.
Eric Valero
Hay otras respuestas aceptadas que proporcionan la pregunta del OP, y se publicaron hace algún tiempo. Cuando publique una respuesta, vea: ¿Cómo escribo una buena respuesta? , asegúrese de agregar una nueva solución o una explicación sustancialmente mejor, especialmente al responder preguntas anteriores.
help-info.de
4

No estoy seguro acerca de la velocidad, pero stringification+ indexOfes un enfoque simple. Comience por convertir su matriz en una cadena:

let strMyArray = JSON.stringify(myArray);

Luego, para una serie de pares de atributos-valores, puede usar:

if (strMyArray.indexOf('"name":"tom"') === -1 && strMyArray.indexOf('"text":"tasty"') === -1) {
   myArray.push({ name: "tom", text: "tasty" });
}

Encontrar un objeto completo es más simple:

if (strMyArray.indexOf(JSON.stringify(objAddMe) === -1) { 
   myArray.push(objAddMe);
}
Moshefnord
fuente
3

En caso de que necesite algo simple sin querer extender el prototipo de matriz:

// Example array
var array = [{id: 1}, {id: 2}, {id: 3}];

function pushIfNew(obj) {
  for (var i = 0; i < array.length; i++) {
    if (array[i].id === obj.id) { // modify whatever property you need
      return;
    }
  }
  array.push(obj);
}
docta_faustus
fuente
3

En caso de que alguien tenga requisitos menos complicados, aquí está mi adaptación de la respuesta para una matriz de cadenas simple:

Array.prototype.pushIfNotExist = function(val) {
    if (typeof(val) == 'undefined' || val == '') { return; }
    val = $.trim(val);
    if ($.inArray(val, this) == -1) {
        this.push(val);
    }
};

Actualización: reemplazado indexOf y trim con alternativas jQuery para la compatibilidad de IE8

Abogado del diablo
fuente
es una buena solución, pero ¿por qué usar trim?
Dejan Dozet
2

Utilicé map and reduce para hacer esto en el caso en que desea buscar por la propiedad específica de un objeto, útil ya que a menudo falla la igualdad directa de objetos.

var newItem = {'unique_id': 123};
var searchList = [{'unique_id' : 123}, {'unique_id' : 456}];

hasDuplicate = searchList
   .map(function(e){return e.unique_id== newItem.unique_id})
   .reduce(function(pre, cur) {return pre || cur});

if (hasDuplicate) {
   searchList.push(newItem);
} else {
   console.log("Duplicate Item");
}
Adrian Smith
fuente
2

Breve ejemplo:

if (typeof(arr[key]) === "undefined") {
  arr.push(key);
}
GigolNet Guigolachvili
fuente
1
Incorrecto. No estamos interesados ​​en presionar la tecla, queremos presionar un par nombre-valor, pero solo si aún no existe.
Stefan
2

a es la matriz de objetos que tienes

a.findIndex(x => x.property=="WhateverPropertyYouWantToMatch") <0 ? 
a.push(objectYouWantToPush) : console.log("response if object exists");
Julio Peguero
fuente
1

Puede usar el método findIndex con una función de devolución de llamada y su parámetro "this".

Nota: los navegadores antiguos no conocen findIndex pero hay un polyfill disponible.

Código de muestra (tenga en cuenta que en la pregunta original, un nuevo objeto se empuja solo si ninguno de sus datos está en objetos empujados previamente):

var a=[{name:"tom", text:"tasty"}], b;
var magic=function(e) {
    return ((e.name == this.name) || (e.text == this.text));
};

b={name:"tom", text:"tasty"};
if (a.findIndex(magic,b) == -1)
    a.push(b); // nothing done
b={name:"tom", text:"ugly"};
if (a.findIndex(magic,b) == -1)
    a.push(b); // nothing done
b={name:"bob", text:"tasty"};
if (a.findIndex(magic,b) == -1)
    a.push(b); // nothing done
b={name:"bob", text:"ugly"};
if (a.findIndex(magic,b) == -1)
    a.push(b); // b is pushed into a
Simon hola
fuente
1

Supongo que es demasiado tarde para responder aquí, sin embargo, esto es lo que finalmente se me ocurrió para un administrador de correo que escribí. Trabajos es todo lo que necesito.

window.ListManager = [];
$('#add').click(function(){
//Your Functionality
  let data =Math.floor(Math.random() * 5) + 1 
  
  if (window.ListManager.includes(data)){
      console.log("data exists in list")
  }else{
       window.ListManager.push(data);
  }
  
  
  $('#result').text(window.ListManager);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Unique List</h1>

<p id="result"></p>
<button id="add">Add to List</button>

TheeCodeDragon
fuente
0

Esta es una función de trabajo para una comparación de objetos. En algunos casos, puede tener muchos campos para comparar. Simplemente repita la matriz y llame a esta función con elementos existentes y elementos nuevos.

 var objectsEqual = function (object1, object2) {
        if(!object1 || !object2)
            return false;
        var result = true;
        var arrayObj1 = _.keys(object1);
        var currentKey = "";
        for (var i = 0; i < arrayObj1.length; i++) {
            currentKey = arrayObj1[i];
            if (object1[currentKey] !== null && object2[currentKey] !== null)
                if (!_.has(object2, currentKey) ||
                    !_.isEqual(object1[currentKey].toUpperCase(), object2[currentKey].toUpperCase()))
                    return false;
        }
        return result;
    };
usuario3180914
fuente
0

Aquí tiene una manera de hacerlo en una línea para dos matrices:

const startArray = [1,2,3,4]
const newArray = [4,5,6]

const result = [...startArray, ...newArray.filter(a => !startArray.includes(a))]

console.log(result);
//Result: [1,2,3,4,5,6]
Jöcker
fuente
-1

Puede usar jQuery grep y push si no hay resultados: http://api.jquery.com/jQuery.grep/

Básicamente es la misma solución que en la solución "extender el prototipo", pero sin extender (o contaminar) el prototipo.

jgroenen
fuente
-2

Puede verificar la matriz usando foreach y luego hacer estallar el elemento si existe; de ​​lo contrario, agregue un nuevo elemento ...

muestra newItemValue & submitFields son pares clave, valor

> //submitFields existing array
>      angular.forEach(submitFields, function(item) {
>                   index++; //newItemValue new key,value to check
>                     if (newItemValue == item.value) {
>                       submitFields.splice(index-1,1);
>                         
>                     } });

                submitFields.push({"field":field,"value":value});
Taran
fuente