Array.push () se non esiste?


247

Come posso inserire un array se non esistono valori? Ecco il mio array:

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

Se ho provato a spingere di nuovo nell'array con uno dei due name: "tom"o text: "tasty", non voglio che accada nulla ... ma se nessuno di questi è presente, lo voglio.push()

Come posso fare questo?


5
Utilizzare un dizionario (hash / albero) anziché un array.
Thomas Eding,

Sono tutti disponibili in JavaScript?
rahul,


3
usa un Set
Drax il

1
Il set non funziona con una serie di oggetti
rffaguiar

Risposte:


114

È possibile estendere il prototipo dell'array con un metodo personalizzato:

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

5
Penso che il tuo attivista (comparatore?) Dovrebbe prendere due argomenti, questo semplificherebbe il caso in cui il valore aggiunto è in linea e non in una variabile a cui puoi accedere nella tua funzione. array.pushIfNotExist ({nome: "tom", testo: "gustoso"}, funzione (a, b) {return a.name === b.name && a.text === b.text;});
Vincent Robert,

26
Mi chiedo perché questo non sia nativo della lingua - dimentica come è implementato - l'idea di "aggiungere solo se unici" è così fondamentale da supporre che esista.
justSteve,

9
È meglio estendere il prototipo di array con il metodo JavaScript 1.6 IndexOf invece di inArray.
Eugene Gluhotorenko,

7
Array.findIndex()è una funzione JS integrata che otterrà lo stesso risultato del codice.

4
L'estensione diretta degli oggetti incorporati è una cattiva pratica.
Slava Fomin II

429

Per un array di stringhe (ma non un array di oggetti), è possibile verificare se esiste un elemento chiamando .indexOf()e se non lo fa, basta spingere l'elemento nell'array:

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)


50
Non sono sicuro del perché questo non sia contrassegnato come corretto. Non utilizza alcun esterno, non richiede la creazione di un'estensione ed è estremamente semplice. Risposta perfetta per la domanda operativa.
pimbrouwers,

39
Nella domanda iniziale, i valori dell'array sono oggetti, non stringhe (e questa soluzione non funziona come se i valori fossero oggetti).
Simon Ciao,

12
@EmilPedersen - non proprio. Prova if (a.indexOf({ name: "tom", text: "tasty" })!=-1) a.push({ name: "tom", text: "tasty" })due volte. Aggiungerà due volte un oggetto "simile".
commonpike,

18
Questa risposta dovrebbe essere rimossa poiché è obiettivamente sbagliata, ma ha comunque attirato il maggior numero di voti.
nikola,

2
Questa non è una risposta corretta, perché è accettata? Funziona solo con gli array Js, non con gli oggetti all'interno degli array.
digitai,

100

È abbastanza facile fare usando la Array.findIndexfunzione, che prende una funzione come argomento:

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

8
QUESTA è la risposta ragazzi !!
jafed

2
più rilevante per aggiungere un elemento nell'array, se non presente
akshay bagade,

1
Grazie, cercavo questo dappertutto.
dt192,

41

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

var cleanArray = $.unique(clutteredArray);

potresti essere interessato anche a makeArray

L'esempio precedente è meglio dire che controlla se esiste prima di spingere. Con il senno di poi, afferma anche che puoi dichiararlo come parte del prototipo (immagino che sia anche l'estensione della classe), quindi nessun grande miglioramento di seguito.

Tranne che non sono sicuro che indexOf sia un percorso più veloce di Array? probabilmente.

Array.prototype.pushUnique = function (item){
    if(this.indexOf(item) == -1) {
    //if(jQuery.inArray(item, this) == -1) {
        this.push(item);
        return true;
    }
    return false;
}

22
Dal link jQuery: Note that this only works on arrays of DOM elements, not strings or numbers.Inoltre, indexOf non funziona in IE8 :(
dmathisen

Puoi usare lodash _.indexOf, che funzionerà in IE8
LTroya il

25

Utilizzare una libreria js come underscore.js per questi motivi esattamente. Usa: unione: calcola l'unione degli array passati: l'elenco di elementi univoci, in ordine, presenti in uno o più array.

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]

8
Nota, questo restituisce un nuovo array e in realtà non spinge verso un array esistente.
ilovett,

4
IMHO non c'è davvero bisogno di introdurre un framework per testare qualcosa di così semplice
DrewT il

24

Come questo?

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

Con oggetto

var item = {name: "tom", text: "tasty"}
var array = [{}]
if (!array.find(o => o.name === 'tom' && o.text === 'tasty'))
    array.push(item)

4
array.findè una cattiva idea perché cerca nell'intero array. Usa findIndex, che cerca solo fino alla prima occorrenza.

3
@ K48 secondo questo: stackoverflow.com/a/33759573/5227365 "trova" si ferma dopo aver trovato l'oggetto
Pascal,

19

So che questa è una domanda molto vecchia, ma se stai usando ES6 puoi usare una versione molto piccola:

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

Molto semplice, inizialmente aggiungi un filtro che rimuove l'elemento, se esiste già, e poi lo aggiunge tramite un concat.

Ecco un esempio più realistico:

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

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

Se l'array contiene oggetti, è possibile adattare la funzione filtro in questo modo:

someArray.filter(f => f.some(s => s.id === myId)).concat([{ id: myId }])

3
Piuttosto una soluzione elegante qui. Grazie!
Jonathan Picazo,

18

Spingi in modo dinamico

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

In metodo semplice

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

Se l'array contiene solo tipi primitivi / array semplice

var b = [1, 7, 8, 4, 3];
var newItem = 6;
b.indexOf(newItem) === -1 && b.push(newItem);

2
Salute alle tue mani Soluzione semplice e bella @Gopala raja
naika

11

Vorrei suggerire di utilizzare un set ,

I set consentono solo voci univoche, il che risolve automaticamente il problema.

I set possono essere dichiarati in questo modo:

const baz = new Set(["Foo","Bar"])

Grazie per averlo segnalato @Michael. Buona soluzione per quando vogliamo mantenere dati distinti con il minimo sforzo. FWIW, è importante notare che le prestazioni dell'array sono migliori in quanto richiede meno CPU per recuperare l'elemento quando è necessario.
Ben

La domanda si pone Array.push, quindi Set.addè l'equivalente di quello.
bryc,

9

Codice semplice, se 'indexOf' restituisce '-1' significa che l'elemento non è all'interno dell'array quindi la condizione '=== -1' recupera vero / falso.

L'operatore "&&" significa "e", quindi se la prima condizione è vera la spingiamo nell'array.

array.indexOf(newItem) === -1 && array.push(newItem);

@ D.Lawrence Sì, ora molto meglio.
Eric Valero

Esistono altre risposte accettate che forniscono la domanda del PO e sono state pubblicate qualche tempo fa. Quando invio una risposta, vedi: Come posso scrivere una buona risposta? , assicurati di aggiungere una nuova soluzione o una spiegazione sostanzialmente migliore, soprattutto quando rispondi a domande precedenti.
help-info.de

4

Non sono sicuro della velocità, ma stringification+ indexOfè un approccio semplice. Inizia con la trasformazione dell'array in una stringa:

let strMyArray = JSON.stringify(myArray);

Quindi per una serie di coppie attributo-valore puoi usare:

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

Trovare un intero oggetto è più semplice:

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

3

Nel caso abbiate bisogno di qualcosa di semplice senza voler estendere il prototipo dell'array:

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

3

Nel caso in cui qualcuno abbia requisiti meno complicati, ecco il mio adattamento della risposta per un semplice array di stringhe:

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

Aggiornamento: Sostituito indice di e tagliare con alternative jQuery per compatibilità IE8


è una buona soluzione, ma perché usare trim?
Dejan Dozet,

2

Ho usato la mappa e la riduzione per farlo nel caso in cui si desideri cercare in base a una proprietà specifica di un oggetto, utile come fare l'uguaglianza diretta degli oggetti spesso fallirà.

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

2

Breve esempio:

if (typeof(arr[key]) === "undefined") {
  arr.push(key);
}

1
Non corretto. Non ci interessa spingere la chiave, vogliamo spingere una coppia nome-valore, ma solo se non esiste già.
Stefan,

2

a è la matrice di oggetti che hai

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

1

È possibile utilizzare il metodo findIndex con una funzione di callback e il relativo parametro "this".

Nota: i vecchi browser non conoscono findIndex ma è disponibile un polyfill.

Codice di esempio (fare attenzione che nella domanda originale, un nuovo oggetto venga inviato solo se nessuno dei suoi dati si trova negli oggetti inviati in precedenza):

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

1

Immagino che sia troppo tardi per rispondere qui, ma questo è quello che alla fine ho pensato a un gestore di posta che ho scritto. Funziona tutto ciò di cui ho bisogno.

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>


0

Questa è una funzione funzionante per un confronto di oggetti. In alcuni casi potresti avere molti campi da confrontare. È sufficiente eseguire il loop dell'array e chiamare questa funzione con un oggetto esistente e un nuovo oggetto.

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

0

Qui hai un modo per farlo in una riga per due array:

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]

-1

Puoi usare jQuery grep e spingere se non ci sono risultati: http://api.jquery.com/jQuery.grep/

È sostanzialmente la stessa soluzione della soluzione "estensione del prototipo", ma senza l'estensione (o l'inquinamento) del prototipo.


-2

Puoi controllare l'array usando foreach e poi pop l'elemento se esiste altrimenti aggiungi nuovo elemento ...

esempio newItemValue e submitFields sono coppie chiave-valore

> //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});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.