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.