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ć?


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

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)


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

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

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]

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)

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 }])

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

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

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

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

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

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


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

2

Krótki przykład:

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

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

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

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>


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

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]


-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});
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.