Array.push () se não existir?

247

Como posso inserir uma matriz se nenhum valor existe? Aqui está minha matriz:

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

Se eu tentasse entrar novamente no array com um name: "tom"ou outro text: "tasty", não quero que nada aconteça ... mas se nenhum deles estiver lá, quero que.push()

Como posso fazer isso?

Tarnfeld
fonte
5
Use um dicionário (hash / árvore) em vez de uma matriz.
Thomas Eding
Todos estes estão disponíveis em javascript?
R1 #
3
use um Set
Drax
1
Set não funciona com matriz de objetos
rffaguiar

Respostas:

114

Você pode estender o protótipo Array com um método personalizado:

// 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
fonte
5
Eu acho que o seu camparer (comparador?) Deve ter dois argumentos, isso simplificaria o caso quando o valor agregado estiver embutido e não em uma variável que você possa acessar em sua função. array.pushIfNotExist ({name: "tom", texto: "tasty"}), função (a, b) {retorna a.name === b.name && a.text === b.text;});
Vincent Robert
26
Estou me perguntando por que isso não é nativo da linguagem - esqueça como é implementado - a idéia de 'adicionar apenas se único' é tão fundamental que se supõe que exista.
justSteve
9
É melhor estender o protótipo da matriz com o método JavaScript 1.6 IndexOf em vez do seu inArray.
Eugene Gluhotorenko
7
Array.findIndex()é uma função JS interna que obterá o mesmo que seu código.
4
Estender objetos internos diretamente é uma prática ruim.
Slava Fomin II
429

Para uma matriz de seqüências de caracteres (mas não uma matriz de objetos), você pode verificar se um item existe chamando .indexOf()e se não existir , basta enviar o item para a matriz:

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
fonte
50
Não sei por que isso não está marcado como correto. Ele não usa elementos externos, não requer a criação de uma extensão e é simples. Resposta perfeita para a questão das operações.
Pimbrouwers
39
Na pergunta inicial, os valores da matriz são objetos, não seqüências de caracteres (e essa solução não funciona como se os valores fossem objetos).
Simon Hi
12
@EmilPedersen - na verdade não. Tente if (a.indexOf({ name: "tom", text: "tasty" })!=-1) a.push({ name: "tom", text: "tasty" })duas vezes. Ele adicionará um objeto 'semelhante' duas vezes.
commonpike
18
Esta resposta deve ser removida, pois está objetivamente errada, mas ainda atraiu o maior número de votos.
Nikola
2
Esta não é uma resposta correta, por que é aceita? Funciona apenas para matrizes Js, não para objetos dentro de matrizes.
digitai
100

É bastante fácil usar a Array.findIndexfunção, que aceita uma função como argumento:

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
fonte
8
Esta é a resposta pessoal !!
Jafed4
2
mais relevantes para adicionar um elemento na matriz se não estiver presente
akshay bagade
1
Obrigado, estava procurando isso em todos os lugares.
dt192 30/05/19
41

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

var cleanArray = $.unique(clutteredArray);

você também pode estar interessado em makeArray

O exemplo anterior é melhor dizer que verifique se existe antes de pressionar. Em retrospectiva, também afirma que você pode declará-lo como parte do protótipo (acho que é conhecido como Extensão de Classe), portanto, não há grande aprimoramento abaixo.

Exceto que não tenho certeza se indexOf é uma rota mais rápida que o inArray? provavelmente.

Array.prototype.pushUnique = function (item){
    if(this.indexOf(item) == -1) {
    //if(jQuery.inArray(item, this) == -1) {
        this.push(item);
        return true;
    }
    return false;
}
MistereeDevlord
fonte
22
No link jQuery: Note that this only works on arrays of DOM elements, not strings or numbers.Além disso, o indexOf não funciona no IE8 :(
dmathisen
Você pode usar o lodash _.indexOf, que funcionará no IE8
LTroya 4/16
25

Use uma biblioteca js como underscore.js por esses motivos exatamente. Uso: união: Calcula a união das matrizes passadas: a lista de itens exclusivos, em ordem, que estão presentes em uma ou mais matrizes.

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]
Ronen Rabinovici
fonte
8
Observe que isso retorna uma nova matriz e, na verdade, não envia para uma matriz existente.
precisa saber é
4
IMHO realmente não há necessidade de criar uma estrutura para testar algo tão simples #
DrewT 30/12/15
24

Como isso?

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

Com objeto

var item = {name: "tom", text: "tasty"}
var array = [{}]
if (!array.find(o => o.name === 'tom' && o.text === 'tasty'))
    array.push(item)
Ronnie Royston
fonte
4
array.findé uma péssima ideia, porque pesquisa toda a matriz. Use findIndex, que pesquisa apenas até a primeira ocorrência.
3
@ K48 de acordo com o seguinte: stackoverflow.com/a/33759573/5227365 "find" para depois de encontrar o item
Pascal
19

Sei que essa é uma pergunta muito antiga, mas se você estiver usando o ES6, poderá usar uma versão muito pequena:

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

Muito fácil, primeiro adicione um filtro que remova o item - se ele já existir, e adicione-o através de uma concat.

Aqui está um exemplo mais realista:

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

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

Se seu array contiver objetos, você poderá adaptar a função de filtro da seguinte forma:

someArray.filter(f => f.some(s => s.id === myId)).concat([{ id: myId }])
Michael J. Zoidl
fonte
3
Uma solução bastante elegante aqui. Obrigado!
Jonathan Picazo
18

Empurre dinamicamente

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

No método simples

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

Se a matriz contiver apenas tipos primitivos / matriz simples

var b = [1, 7, 8, 4, 3];
var newItem = 6;
b.indexOf(newItem) === -1 && b.push(newItem);
Gopala raja naika
fonte
2
Saúde para o seu hands.Simple e bela solução @Gopala raja Naika
Nasimba
11

Eu sugiro que você use um conjunto ,

Os conjuntos permitem apenas entradas exclusivas, que resolvem automaticamente o seu problema.

Os conjuntos podem ser declarados da seguinte forma:

const baz = new Set(["Foo","Bar"])
Michael McQuade
fonte
Obrigado por apontar isso para @ Michael. Boa solução para quando queremos manter dados distintos com o mínimo de esforço. FWIW, é importante observar que o desempenho da matriz é melhor, pois requer menos CPU para buscar o elemento quando necessário.
Ben
A pergunta é sobre Array.push, então Set.addé o equivalente a isso.
bryc 25/05
9

Código fácil, se 'indexOf' retornar '-1' significa que o elemento não está dentro da matriz, e a condição '=== -1' recuperará verdadeiro / falso.

O operador '&&' significa 'e', ​​portanto, se a primeira condição for verdadeira, enviamos para a matriz.

array.indexOf(newItem) === -1 && array.push(newItem);
Eric Valero
fonte
@ D.Lawrence Sim, muito melhor agora.
Eric Valero
Existem outras respostas aceitas que fornecem a pergunta do OP e foram publicadas há algum tempo. Ao postar uma resposta, consulte: Como redigir uma boa resposta? , adicione uma nova solução ou uma explicação substancialmente melhor, especialmente ao responder a perguntas mais antigas.
help-info.de
4

Não tenho certeza sobre velocidade, mas stringification+ indexOfé uma abordagem simples. Comece transformando sua matriz em uma string:

let strMyArray = JSON.stringify(myArray);

Em seguida, para uma série de pares atributo-valor, você pode usar:

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

Encontrar um objeto inteiro é mais simples:

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

Caso você precise de algo simples sem querer estender o protótipo 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
fonte
3

Caso alguém tenha requisitos menos complicados, eis a minha adaptação da resposta para uma matriz simples de strings:

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

Atualização: indexOf e aparagem substituídos por alternativas jQuery para compatibilidade com o IE8

Advogado do diabo
fonte
é uma boa solução, mas por que usar aparar?
Dejan Dozet
2

Usei map e reduzi para fazer isso no caso em que você deseja pesquisar por uma propriedade específica de um objeto, útil como fazer a igualdade direta de objetos geralmente falhará.

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
fonte
2

Breve exemplo:

if (typeof(arr[key]) === "undefined") {
  arr.push(key);
}
GigolNet Guigolachvili
fonte
1
Incorreto. Não estamos interessados ​​em pressionar a tecla, queremos pressionar um par nome-valor, mas apenas se ele ainda não existir.
31518 Stefan
2

a é a matriz de objetos que você possui

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

Você pode usar o método findIndex com uma função de retorno de chamada e seu parâmetro "this".

Nota: navegadores antigos não conhecem o findIndex, mas um polyfill está disponível.

Código de amostra (lembre-se de que, na pergunta original, um novo objeto seja enviado apenas se nenhum dos seus dados estiver em objetos enviados anteriormente):

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
fonte
1

Eu acho que estou atrasado para responder aqui, no entanto, é isso que eu finalmente criei para um gerente de correio que escrevi. Funciona é tudo o que eu preciso.

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
fonte
0

Isso está funcionando func para uma comparação de objetos. Em alguns casos, você pode ter muitos campos para comparar. Basta fazer um loop na matriz e chamar essa função com itens existentes e novo item.

 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
fonte
0

Aqui você tem uma maneira de fazer isso em uma linha para duas matrizes:

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
fonte
-1

Você pode usar o jQuery grep e pressionar se não houver resultados: http://api.jquery.com/jQuery.grep/

É basicamente a mesma solução da solução "estendendo o protótipo", mas sem estender (ou poluir) o protótipo.

jgroenen
fonte
-2

Você pode verificar a matriz usando foreach e, em seguida, exibir o item, se existir, caso contrário, adicione um novo item ...

exemplo newItemValue e submitFields são pares chave, valor

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