Array.push (), якщо він не існує?


247

Як я можу натиснути на масив, якщо жодне значення не існує? Ось мій масив:

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

Якщо я спробую знову натиснути на масив з будь-яким name: "tom"або text: "tasty", я не хочу, щоб щось сталося ... але якщо жодного з них немає, то я хочу, щоб це.push()

Як я можу це зробити?


5
Використовуйте словник (хеш / дерево) замість масиву.
Томас Едінг

Чи все це доступно в JavaScript?
rahul


3
використовувати набір
Drax

1
Набір не працює з масивом об'єктів
rffaguiar

Відповіді:


114

Ви можете розширити прототип масиву спеціальним методом:

// 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
Я думаю, що ваш артист (компаратор?) Повинен взяти два аргументи, це спростить випадок, коли додана вартість є вбудованою, а не в змінній, до якої ви можете отримати доступ у своїй функції. array.pushIfNotExist ({ім'я: "tom", текст: "смачно"}, функція (a, b) {return a.name === b.name && a.text === b.text;});
Вінсент Роберт

26
Мені цікаво, чому ця мова не є рідною для мови - забудьте, як вона реалізована - ідея "додавати лише те, що є унікальним" настільки фундаментальна, що можна вважати, що існує.
justSteve

9
Краще розширити прототип Array методом JavaScript 1.6 IndexOf замість вашого inArray.
Євген Глухотеренко

7
Array.findIndex()це вбудована функція JS, яка досягне того ж, що і ваш код.

4
Розширення вбудованих об'єктів безпосередньо - це погана практика.
Слава Фомін II

429

Для масиву рядків (але не для масиву об'єктів) ви можете перевірити, чи існує елемент, зателефонувавши, .indexOf()а якщо він не є, просто натисніть на елемент у масив:

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
Не впевнений, чому це не позначено як правильне. Він не використовує жодних зовнішніх, не вимагає створення розширення і просто мертвий. Ідеальна відповідь на питання ops.
pimbrouwers

39
У початковому питанні значення масиву - це об'єкти, а не рядки (і це рішення не працює так, як якщо б значення були об'єктами).
Саймон Привіт

12
@EmilPedersen - не дуже. Спробуйте if (a.indexOf({ name: "tom", text: "tasty" })!=-1) a.push({ name: "tom", text: "tasty" })двічі. Він додасть «подібний» об’єкт двічі.
commonpike

18
Цю відповідь слід усунути, оскільки вона є об'єктивно неправильною, але все ж привертає найбільшу кількість відгуків.
nikola

2
Це не правильна відповідь, чому це прийнято? Він просто працює з масивами Js, а не об'єктом в масивах.
digitai

100

Це досить легко зробити за допомогою Array.findIndexфункції, яка приймає функцію як аргумент:

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
ЦЕ відповідь, хлопці !!
jafed

2
найрелевантніше додати елемент до масиву, якщо його немає
akshay bagade

1
Спасибі, шукали цього скрізь.
dt192

41

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

var cleanArray = $.unique(clutteredArray);

вас також може зацікавити makeArray

Попередній приклад найкраще сказати, що перед натисканням перевірте, чи існує він. Я зауважую, що він також стверджує, що ви можете оголосити його як частину прототипу (я вважаю, що це aka Extension Class), тому внизу немає великого вдосконалення.

За винятком того, що я не впевнений, що indexOf - більш швидкий маршрут, ніж inArray? ймовірно.

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

22
З посилання jQuery: Note that this only works on arrays of DOM elements, not strings or numbers.Також indexOf не працює в IE8 :(
dmathisen

Ви можете використовувати lodash _.indexOf, який працюватиме в IE8
LTroya

25

Саме з цих причин використовуйте бібліотеку js, як underscore.js . Використання: union: Обчислює об'єднання переданих масивів: перелік унікальних елементів у порядку, які присутні в одному або декількох масивах.

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

8
Зауважте, що це повертає новий масив і насправді не натискає на існуючий масив.
ilovett

4
ІМХО насправді не потрібно запроваджувати рамки для тестування на щось таке просте
DrewT

24

Подобається це?

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

З об’єктом

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

4
array.findце погана ідея, оскільки він шукає весь масив. Використовуйте findIndex, яке здійснює пошук лише до першого появи.

3
@ K48 відповідно до цього: stackoverflow.com/a/33759573/5227365 "знайти" зупиняється після того, як він знайшов предмет
Паскаль

19

Я знаю, що це дуже давнє запитання, але якщо ви використовуєте ES6, ви можете використовувати дуже маленьку версію:

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

Дуже просто: спочатку додайте фільтр, який вилучає елемент - якщо він вже існує, а потім додайте його через конмат.

Ось більш реалістичний приклад:

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

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

Якщо масив містить об'єкти, ви можете адаптувати функцію фільтра так:

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

3
Тут досить елегантне рішення. Дякую!
Джонатан

18

Натисніть динамічно

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

Простим методом

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

Якщо масив містить лише примітивні типи / простий масив

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

2
Здоров’я до рук. Просте і красиве рішення @Gopala raja naika
Nasimba

11

Я б запропонував вам використовувати набір ,

Набори дозволяють лише унікальні записи, що автоматично вирішує вашу проблему.

Набори можна оголосити так:

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

Дякуємо, що вказали на це @Michael. Хороше рішення, коли ми хочемо з мінімальними зусиллями підтримувати різні дані. FWIW, важливо зауважити, що продуктивність масиву краща, оскільки для отримання елемента потрібно менше процесора.
Бен

Питання задається про Array.push, так Set.addце рівнозначно тому.
bryc

9

Легкий код, якщо 'indexOf' повертає '-1', це означає, що елемент не знаходиться всередині масиву, тоді умова '=== -1' отримує значення true / false.

Оператор '&& означає' і ', тож якщо перша умова справжня, ми підштовхуємо її до масиву.

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

@ D.Lawrence Так, зараз набагато краще.
Ерік Валеро

Є й інші прийняті відповіді, які забезпечують питання ОП, і вони були опубліковані деякий час тому. Опублікувавши відповідь, див.: Як написати гарну відповідь? , переконайтеся, що ви додали або нове рішення, або істотно краще пояснення, особливо, відповідаючи на старіші запитання.
help-info.de

4

Не впевнений у швидкості, але stringification+ indexOf- простий підхід. Почніть з перетворення масиву в рядок:

let strMyArray = JSON.stringify(myArray);

Тоді для серії пар атрибут-значення ви можете використовувати:

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

Пошук цілого об'єкта простіше:

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

3

Якщо вам потрібно щось просте, не бажаючи подовжувати прототип масиву:

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

Якщо у когось є менш складні вимоги, ось моя адаптація відповіді на простий масив рядків:

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

Оновлення: Замінено indexOf та обробку альтернативами jQuery для сумісності IE8


це приємне рішення, але навіщо використовувати обробку?
Деян Дозет

2

Я використав карту і зменшити для цього в тому випадку, коли ви хочете здійснити пошук за певною властивістю об'єкта, корисною при виконанні прямої рівності об'єкта часто не вдасться.

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

Короткий приклад:

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

1
Неправильно. Нам не цікаво натискання клавіші, ми хочемо натиснути пару імен-значень, але лише в тому випадку, якщо вона вже не існує.
Стефан

2

a - це масив об'єктів, які ви маєте

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

1

Ви можете використовувати метод findIndex з функцією зворотного виклику та його параметром "це".

Примітка: старі веб-переглядачі не знають findIndex, але поліфіл доступний.

Зразок коду (подбайте про те, щоб в оригінальному запитанні новий об’єкт висувався лише в тому випадку, якщо жодна його інформація не знаходиться в превентивних висунутих об'єктах):

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

Я думаю, я занадто пізно, щоб відповісти тут, але це те, що я нарешті придумав для менеджера пошти, який я написав. Працює, це все, що мені потрібно.

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

Це працює функція для порівняння об'єктів. У деяких випадках у вас може бути багато полів для порівняння. Просто обведіть масив і зателефонуйте до цієї функції за допомогою існуючих елементів та нового елемента.

 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

Тут у вас є спосіб зробити це в одному рядку для двох масивів:

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

Ви можете використовувати jQuery grep і push, якщо результатів немає: http://api.jquery.com/jQuery.grep/

Це в основному те саме рішення, що і в рішенні "розширення прототипу", але без розширення (або забруднення) прототипу.


-2

Ви можете перевірити масив, використовуючи функцію foreach, а потім спливати елемент, якщо він існує, інакше додати новий елемент ...

зразок newItemValue & submitFields є ключовими, парами значень

> //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});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.