Array.push () jeśli nie istnieje?

247

Jak mogę wypchnąć do tablicy, jeśli żadna z wartości nie istnieje? Oto moja tablica:

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

Jeśli spróbuję ponownie wcisnąć do tablicy za pomocą albo, name: "tom"albo text: "tasty"nie chcę, żeby coś się wydarzyło ... ale jeśli żadnego z nich nie ma, to chcę.push()

W jaki sposób mogę to zrobić?

Tarnfeld
źródło
5
Użyj słownika (skrótu / drzewa) zamiast tablicy.
Thomas Eding
Czy wszystkie są dostępne w javascript?
rahul
3
użyj zestawu
Drax
1
Zestaw nie działa z szeregiem obiektów
rffaguiar

Odpowiedzi:

114

Możesz rozszerzyć prototyp Array za pomocą niestandardowej metody:

// 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
źródło
5
Myślę, że twój Camparer (komparator?) Powinien wziąć dwa argumenty, co uprościłoby przypadek, gdy wartość dodana jest wbudowana, a nie w zmiennej, do której możesz uzyskać dostęp w swojej funkcji. array.pushIfNotExist ({name: "tom", text: "tasty"}, funkcja (a, b) {return a.name === b.name && a.text === b.text;});
Vincent Robert
26
Zastanawiam się, dlaczego nie jest to język ojczysty - zapomnij o tym, jak to jest zaimplementowane - pomysł „dodawania tylko wtedy, gdy jest niepowtarzalny” jest tak fundamentalny, że można go założyć.
justSteve,
9
Lepiej jest rozszerzyć prototyp Array o metodę JavaScript 1.6 IndexOf zamiast inArray.
Eugene Gluhotorenko
7
Array.findIndex()jest wbudowaną funkcją JS, która osiągnie to samo, co Twój kod.
4
Bezpośrednie rozszerzanie wbudowanych obiektów jest złą praktyką.
Slava Fomin II
429

W przypadku tablicy ciągów (ale nie tablicy obiektów) możesz sprawdzić, czy element istnieje, wywołując go, .indexOf()a jeśli nie, to po prostu wepchnij element do tablicy:

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
źródło
50
Nie jestem pewien, dlaczego nie jest to oznaczone jako poprawne. Nie używa żadnych zewnętrznych elementów, nie wymaga tworzenia rozszerzenia i jest bardzo prosty. Idealna odpowiedź na pytanie operacyjne.
pimbrouwers,
39
W początkowym pytaniu wartości tablicy są obiektami, a nie ciągami (a to rozwiązanie nie działa tak, jak w przypadku wartości jako obiektów).
Simon Hi
12
@EmilPedersen - nie bardzo. Spróbuj if (a.indexOf({ name: "tom", text: "tasty" })!=-1) a.push({ name: "tom", text: "tasty" })dwa razy. Dwukrotnie doda „podobny” obiekt.
commonpike
18
Ta odpowiedź powinna zostać usunięta, ponieważ jest obiektywnie błędna, ale wciąż przyciąga największą liczbę głosów pozytywnych.
nikola
2
To nie jest poprawna odpowiedź, dlaczego jest akceptowana? Po prostu działa na tablice Js, a nie na obiekty wewnątrz tablic.
digitai
100

Jest to dość łatwe przy użyciu Array.findIndexfunkcji, która przyjmuje funkcję jako 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
źródło
8
To jest odpowiedź chłopaki !!
jafed
2
najbardziej odpowiednie, aby dodać element do tablicy, jeśli nie jest obecny
akshay bagade
1
Dzięki, szukałem tego wszędzie.
dt192
41

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

var cleanArray = $.unique(clutteredArray);

możesz być także zainteresowany makeArray

Poprzedni przykład najlepiej powiedzieć, że sprawdź, czy istnieje przed wypchnięciem. Z perspektywy czasu widzę też, że możesz zadeklarować go jako część prototypu (tak się składa, że ​​jest to również rozszerzenie klasy), więc poniżej nie ma dużego rozszerzenia.

Tyle że nie jestem pewien, czy indeksOf jest szybszą trasą niż InArray? prawdopodobnie.

Array.prototype.pushUnique = function (item){
    if(this.indexOf(item) == -1) {
    //if(jQuery.inArray(item, this) == -1) {
        this.push(item);
        return true;
    }
    return false;
}
MistereeDevlord
źródło
22
Z linku jQuery: Note that this only works on arrays of DOM elements, not strings or numbers.Również indexOf nie działa w IE8 :(
dmathisen
Możesz użyć lodash _.indexOf, który będzie działał w IE8
LTroya
25

Z tych powodów używaj biblioteki js, takiej jak underscore.js . Użyj: union: Oblicza sumę przekazanych tablic: listę unikalnych elementów, w kolejności, które są obecne w jednej lub więcej tablicach.

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]
Ronen Rabinovici
źródło
8
Zauważ, że zwraca nową tablicę i nie wypycha do istniejącej tablicy.
ilovett
4
IMHO naprawdę nie trzeba wprowadzać frameworka, aby przetestować coś tak prostego
DrewT
24

Lubię to?

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

Z przedmiotem

var item = {name: "tom", text: "tasty"}
var array = [{}]
if (!array.find(o => o.name === 'tom' && o.text === 'tasty'))
    array.push(item)
Ronnie Royston
źródło
4
array.findto zły pomysł, ponieważ przeszukuje całą tablicę. Użyj findIndex, która wyszukuje tylko do pierwszego wystąpienia.
3
@ K48 zgodnie z tym: stackoverflow.com/a/33759573/5227365 „znajdź” zatrzymuje się po znalezieniu przedmiotu
Pascal
19

Wiem, że to bardzo stare pytanie, ale jeśli używasz ES6, możesz użyć bardzo małej wersji:

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

Bardzo łatwo, najpierw dodaj filtr, który usuwa element - jeśli już istnieje, a następnie dodaj go przez konkat.

Oto bardziej realistyczny przykład:

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

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

Jeśli tablica zawiera obiekty, możesz dostosować funkcję filtrowania w następujący sposób:

someArray.filter(f => f.some(s => s.id === myId)).concat([{ id: myId }])
Michael J. Zoidl
źródło
3
Całkiem eleganckie rozwiązanie tutaj. Dzięki!
Jonathan Picazo
18

Pchaj dynamicznie

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

W prosty sposób

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

Jeśli tablica zawiera tylko typy pierwotne / tablica prosta

var b = [1, 7, 8, 4, 3];
var newItem = 6;
b.indexOf(newItem) === -1 && b.push(newItem);
Gopala raja naika
źródło
2
Zdrowie w Twoich rękach. Proste i piękne rozwiązanie @Gopala raja naika
Nasimba
11

Proponuję użyć zestawu ,

Zestawy dopuszczają tylko unikalne wpisy, co automatycznie rozwiązuje problem.

Zestawy można deklarować w następujący sposób:

const baz = new Set(["Foo","Bar"])
Michael McQuade
źródło
Dzięki za zwrócenie na to uwagi @Michael. Dobre rozwiązanie, gdy chcemy zachować odrębne dane przy minimalnym wysiłku. FWIW, należy zauważyć, że wydajność macierzy jest lepsza, ponieważ wymaga mniej procesora, aby pobrać element, gdy jest potrzebny.
Ben
Pytanie dotyczy Array.push, więc Set.addjest to odpowiednik tego.
bryc
9

Łatwy kod, jeśli „indexOf” zwraca „-1”, oznacza to, że elementu nie ma w tablicy, wówczas warunek „=== -1” odzyskuje wartość prawda / fałsz.

Operator „&&” oznacza „i”, więc jeśli pierwszy warunek jest spełniony, wypychamy go do tablicy.

array.indexOf(newItem) === -1 && array.push(newItem);
Eric Valero
źródło
@ D.Lawrence Tak, teraz o wiele lepiej.
Eric Valero,
Istnieją inne akceptowane odpowiedzi, które dostarczają pytania PO, i zostały opublikowane jakiś czas temu. Publikując odpowiedź zobacz: Jak napisać dobrą odpowiedź? , upewnij się, że dodajesz nowe rozwiązanie lub znacznie lepsze wyjaśnienie, zwłaszcza w przypadku odpowiedzi na starsze pytania.
help-info.de
4

Nie jestem pewien prędkości, ale stringification+ indexOfto proste podejście. Zacznij od przekształcenia tablicy w ciąg:

let strMyArray = JSON.stringify(myArray);

Następnie dla serii par atrybut-wartość możesz użyć:

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

Znalezienie całego obiektu jest prostsze:

if (strMyArray.indexOf(JSON.stringify(objAddMe) === -1) { 
   myArray.push(objAddMe);
}
moshefnord
źródło
3

Jeśli potrzebujesz czegoś prostego bez potrzeby rozszerzania prototypu 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
źródło
3

W przypadku, gdy ktoś ma mniej skomplikowane wymagania, oto moja adaptacja odpowiedzi dla prostej tablicy ciągów:

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

Aktualizacja: Zamieniono indexOf i trim na alternatywy jQuery dla kompatybilności z IE8

Adwokat diabła
źródło
to dobre rozwiązanie, ale po co używać wykończenia?
Dejan Dozet
2

Użyłem mapy i pomniejszenia, aby to zrobić w przypadku, gdy chcesz wyszukać według konkretnej właściwości obiektu, co jest przydatne, ponieważ wykonywanie bezpośredniej równości obiektów często kończy się niepowodzeniem.

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
źródło
2

Krótki przykład:

if (typeof(arr[key]) === "undefined") {
  arr.push(key);
}
GigolNet Guigolachvili
źródło
1
Niepoprawne. Nie jesteśmy zainteresowani wrzucaniem klucza, chcemy wcisnąć parę nazwa-wartość, ale tylko wtedy, gdy jeszcze nie istnieje.
Stefan
2

a to tablica obiektów, które masz

a.findIndex(x => x.property=="WhateverPropertyYouWantToMatch") <0 ? 
a.push(objectYouWantToPush) : console.log("response if object exists");
Julio Peguero
źródło
1

Możesz użyć metody findIndex z funkcją zwrotną i jej parametrem „this”.

Uwaga: stare przeglądarki nie znają funkcji findIndex, ale dostępny jest pakiet wypełniający.

Przykładowy kod (pamiętaj, że w pierwotnym pytaniu nowy obiekt jest wypychany tylko wtedy, gdy żaden z jego danych nie znajduje się w uprzednio popychanych obiektach):

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 Cześć
źródło
1

Chyba spóźniłem się z odpowiedzią tutaj, ale w końcu wymyśliłem napisanego przez niego menedżera poczty. Działa to wszystko, czego potrzebuję.

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
źródło
0

Działa to func do porównywania obiektów. W niektórych przypadkach możesz mieć wiele pól do porównania. Wystarczy zapętlić tablicę i wywołać tę funkcję z istniejącymi elementami i nowym przedmiotem.

 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;
    };
użytkownik3180914
źródło
0

Tutaj możesz to zrobić w jednym wierszu dla dwóch tablic:

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
źródło
-1

Możesz użyć jQuery grep i push, jeśli nie ma wyników: http://api.jquery.com/jQuery.grep/

Jest to w zasadzie to samo rozwiązanie, co w rozwiązaniu „przedłużanie prototypu”, ale bez przedłużania (lub zanieczyszczania) prototypu.

jgroenen
źródło
-2

Możesz sprawdzić tablicę za pomocą foreach, a następnie pop element, jeśli istnieje, w przeciwnym razie dodaj nowy element ...

sample newItemValue i submFields to pary kluczy i wartości

> //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
źródło