Array.push () si n'existe pas?

247

Comment puis-je pousser dans un tableau si aucune valeur n'existe? Voici mon tableau:

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

Si j'ai essayé de pousser à nouveau dans le tableau avec l'un name: "tom"ou l' autre text: "tasty", je ne veux pas qu'il se passe quoi que ce soit ... mais si aucun d'eux n'est là, alors je veux que cela se produise.push()

Comment puis-je faire ceci?

tarnfeld
la source
5
Utilisez un dictionnaire (hachage / arbre) au lieu d'un tableau.
Thomas Eding, du
Tous ces éléments sont-ils disponibles en javascript?
rahul
3
utiliser un Set
Drax
1
L'ensemble ne fonctionne pas avec un tableau d'objets
rffaguiar

Réponses:

114

Vous pouvez étendre le prototype Array avec une méthode personnalisée:

// 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
la source
5
Je pense que votre campeur (comparateur?) Devrait prendre deux arguments, cela simplifierait le cas lorsque la valeur ajoutée est en ligne et non dans une variable à laquelle vous pouvez accéder dans votre fonction. array.pushIfNotExist ({name: "tom", text: "tasty"}, function (a, b) {return a.name === b.name && a.text === b.text;});
Vincent Robert
26
Je me demande pourquoi ce n'est pas natif de la langue - oubliez comment il est mis en œuvre - l'idée de «n'ajouter que si unique» est si fondamentale qu'on suppose qu'elle existe.
justSteve
9
Il est préférable d'étendre le prototype Array avec la méthode JavaScript 1.6 IndexOf au lieu de votre inArray.
Eugene Gluhotorenko
7
Array.findIndex()est une fonction JS intégrée qui réalisera la même chose que votre code.
4
L'extension directe d'objets intégrés est une mauvaise pratique.
Slava Fomin II
429

Pour un tableau de chaînes (mais pas un tableau d'objets), vous pouvez vérifier si un élément existe en appelant .indexOf()et s'il ne le fait pas simplement pousser l'élément dans le tableau:

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
la source
50
Je ne sais pas pourquoi cela n'est pas signalé comme correct. Il n'utilise aucun externe, ne nécessite pas de créer une extension et est d'une simplicité déconcertante. Réponse parfaite à la question des opérations.
pimbrouwers
39
Dans la question initiale, les valeurs du tableau sont des objets, pas des chaînes (et cette solution ne fonctionne pas comme si les valeurs étaient des objets).
Simon Hi
12
@EmilPedersen - pas vraiment. Essayez if (a.indexOf({ name: "tom", text: "tasty" })!=-1) a.push({ name: "tom", text: "tasty" })deux fois. Il ajoutera un objet «similaire» deux fois.
commonpike
18
Cette réponse devrait être supprimée car elle est objectivement erronée, mais elle a quand même attiré le plus grand nombre de votes positifs.
nikola
2
Ce n'est pas une bonne réponse, pourquoi est-elle acceptée? Cela ne fonctionne que pour les tableaux Js, pas pour les objets dans les tableaux.
digitai
100

C'est assez facile à faire en utilisant la Array.findIndexfonction, qui prend une fonction comme argument:

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
la source
8
CECI est la réponse les gars !!
jafed
2
le plus pertinent pour ajouter un élément dans le tableau s'il n'est pas présent
akshay bagade
1
Merci, je cherchais ça partout.
dt192
41

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

var cleanArray = $.unique(clutteredArray);

vous pourriez également être intéressé par makeArray

L'exemple précédent est préférable de dire que vérifier s'il existe avant de pousser. Avec le recul, il indique également que vous pouvez le déclarer comme faisant partie du prototype (je suppose que c'est alias Extension de classe), donc pas de grande amélioration ci-dessous.

Sauf que je ne sais pas si indexOf est un itinéraire plus rapide que inArray? Probablement.

Array.prototype.pushUnique = function (item){
    if(this.indexOf(item) == -1) {
    //if(jQuery.inArray(item, this) == -1) {
        this.push(item);
        return true;
    }
    return false;
}
MistereeDevlord
la source
22
À partir du lien jQuery: Note that this only works on arrays of DOM elements, not strings or numbers.En outre, indexOf ne fonctionne pas dans IE8 :(
dmathisen
Vous pouvez utiliser lodash _.indexOf, qui fonctionnera dans IE8
LTroya
25

Utilisez une bibliothèque js comme underscore.js pour ces raisons exactement. Utilisation: union: calcule l'union des tableaux passés: la liste des éléments uniques, dans l'ordre, qui sont présents dans un ou plusieurs des tableaux.

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]
Ronen Rabinovici
la source
8
Remarque, cela renvoie un nouveau tableau et ne pousse pas réellement vers un tableau existant.
ilovett
4
À
mon humble avis,
24

Comme ça?

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

Avec objet

var item = {name: "tom", text: "tasty"}
var array = [{}]
if (!array.find(o => o.name === 'tom' && o.text === 'tasty'))
    array.push(item)
Ronnie Royston
la source
4
array.findest une mauvaise idée car il recherche l'ensemble du tableau. Utilisez findIndex, qui recherche uniquement jusqu'à la première occurrence.
3
@ K48 selon ceci: stackoverflow.com/a/33759573/5227365 "find" s'arrête après avoir trouvé l'article
Pascal
19

Je sais que c'est une très vieille question, mais si vous utilisez ES6, vous pouvez utiliser une toute petite version:

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

Très facile, ajoutez d'abord un filtre qui supprime l'élément - s'il existe déjà, puis ajoutez-le via un concat.

Voici un exemple plus réaliste:

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

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

Si votre tableau contient des objets, vous pouvez adapter la fonction de filtre comme ceci:

someArray.filter(f => f.some(s => s.id === myId)).concat([{ id: myId }])
Michael J. Zoidl
la source
3
Une solution assez élégante ici. Merci!
Jonathan Picazo
18

Poussez dynamiquement

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 méthode simple

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

Si le tableau contient uniquement des types primitifs / tableau simple

var b = [1, 7, 8, 4, 3];
var newItem = 6;
b.indexOf(newItem) === -1 && b.push(newItem);
Gopala raja naika
la source
2
Santé à vos mains. Solution simple et belle @ Gopala raja naika
Nasimba
11

Je vous suggère d'utiliser un ensemble ,

Les ensembles n'autorisent que des entrées uniques, ce qui résout automatiquement votre problème.

Les ensembles peuvent être déclarés comme suit:

const baz = new Set(["Foo","Bar"])
Michael McQuade
la source
Merci d'avoir signalé @Michael. Bonne solution lorsque nous voulons conserver des données distinctes avec un minimum d'effort. FWIW, il est important de noter que les performances du tableau sont meilleures car elles nécessitent moins de CPU pour récupérer l'élément lorsque cela est nécessaire.
Ben
La question porte sur Array.push, Set.addest donc l'équivalent de cela.
bryc
9

Code facile, si 'indexOf' renvoie '-1' cela signifie que l'élément n'est pas à l'intérieur du tableau alors la condition '=== -1' récupère vrai / faux.

L'opérateur '&&' signifie 'et', donc si la première condition est vraie, nous la poussons vers le tableau.

array.indexOf(newItem) === -1 && array.push(newItem);
Eric Valero
la source
@ D.Lawrence Oui, tellement mieux maintenant.
Eric Valero
Il existe d'autres réponses acceptées qui fournissent la question du PO, et elles ont été publiées il y a quelque temps. Lors de la publication d'une réponse, voir: Comment puis-je écrire une bonne réponse? , assurez-vous d'ajouter une nouvelle solution ou une meilleure explication, en particulier lorsque vous répondez à des questions plus anciennes.
help-info.de
4

Pas sûr de la vitesse, mais stringification+ indexOfest une approche simple. Commencez par transformer votre tableau en chaîne:

let strMyArray = JSON.stringify(myArray);

Ensuite, pour une série de paires attribut-valeur, vous pouvez utiliser:

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

Trouver un objet entier est plus simple:

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

Au cas où vous auriez besoin de quelque chose de simple sans vouloir étendre le prototype 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);
}
docta_faustus
la source
3

Dans le cas où quelqu'un a des exigences moins compliquées, voici mon adaptation de la réponse pour un simple tableau de chaînes:

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

Mise à jour: IndexOf et trim remplacés par des alternatives jQuery pour la compatibilité IE8

L'avocat du diable
la source
c'est une bonne solution, mais pourquoi utiliser la garniture?
Dejan Dozet
2

J'ai utilisé mapper et réduire pour le faire dans le cas où vous souhaitez rechercher par la propriété spécifique d'un objet, utile car faire l'égalité directe d'objet échouera souvent.

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
la source
2

Petit exemple:

if (typeof(arr[key]) === "undefined") {
  arr.push(key);
}
GigolNet Guigolachvili
la source
1
Pas correcte. Nous ne souhaitons pas pousser la clé, nous voulons pousser une paire nom-valeur, mais seulement si elle n'existe pas déjà.
Stefan
2

a est le tableau d'objets que vous avez

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

Vous pouvez utiliser la méthode findIndex avec une fonction de rappel et son paramètre "this".

Remarque: les anciens navigateurs ne connaissent pas findIndex mais un polyfill est disponible.

Exemple de code (veillez à ce que dans la question d'origine, un nouvel objet ne soit poussé que si aucune de ses données ne se trouve dans des objets poussés précédemment):

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 Hi
la source
1

Je suppose que je suis trop tard pour répondre ici, mais c'est ce que j'ai finalement trouvé pour un gestionnaire de messagerie que j'ai écrit. Des œuvres, c'est tout ce dont j'ai besoin.

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
la source
0

Cela fonctionne pour une comparaison d'objets. Dans certains cas, vous pourriez avoir beaucoup de champs à comparer. Bouclez simplement le tableau et appelez cette fonction avec un élément existant et un nouvel élément.

 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;
    };
user3180914
la source
0

Ici, vous avez un moyen de le faire en une seule ligne pour deux tableaux:

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
la source
-1

Vous pouvez utiliser jQuery grep et pousser si aucun résultat: http://api.jquery.com/jQuery.grep/

C'est fondamentalement la même solution que dans la solution "étendre le prototype", mais sans étendre (ni polluer) le prototype.

jgroenen
la source
-2

Vous pouvez vérifier le tableau à l'aide de foreach, puis faire apparaître l'élément s'il existe, sinon ajouter un nouvel élément ...

les exemples newItemValue et submitFields sont des paires clé / valeur

> //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
la source