Estou trabalhando em JavaScript. Gostaria de armazenar uma lista de valores de sequência exclusivos e não ordenados, com as seguintes propriedades:
- uma maneira rápida de perguntar 'está A na lista'?
- uma maneira rápida de fazer 'excluir A da lista, se existir na lista'
- uma maneira rápida de fazer 'adicionar A à lista, se ainda não estiver presente'.
O que eu realmente quero é um conjunto. Alguma sugestão para a melhor maneira de imitar um conjunto em JavaScript?
Esta pergunta recomenda o uso de um Object , com as chaves armazenando propriedades e os valores definidos como true: isso é uma maneira sensata?
javascript
Richard
fonte
fonte
Respostas:
Se você estiver programando em um ambiente compatível com ES6 (como node.js, um navegador específico com os recursos necessários para ES6 ou transpilando código ES6 para o seu ambiente), poderá usar o
Set
objeto incorporado ao ES6 . Possui recursos muito agradáveis e pode ser usado como é certo em seu ambiente.Para muitas coisas simples em um ambiente ES5, o uso de um Object funciona muito bem. Se
obj
é seu objeto eA
é uma variável que possui o valor que você deseja operar no conjunto, você pode fazer o seguinte:Código de inicialização:
Pergunta 1: Está
A
na lista:Pergunta 2: exclua 'A' da lista, se houver:
Pergunta 3: adicione 'A' à lista se ainda não estiver lá
Para completar, o teste para saber se
A
está na lista é um pouco mais seguro com isso:devido a um possível conflito entre métodos e / ou propriedades integradas no objeto base, como a
constructor
propriedadeBarra lateral no ES6: a versão atual do ECMAScript 6 ou algo chamado ES 2015 possui um objeto Set interno . Está implementado agora em alguns navegadores. Desde disponibilidade navegador muda ao longo do tempo, você pode olhar para a linha para
Set
em esta tabela compatibilidade ES6 para ver o status atual disponibilidade browser.Uma vantagem do objeto Set embutido é que ele não coage todas as chaves a uma string como o Object, para que você possa ter 5 e "5" como chaves separadas. E você pode até usar objetos diretamente no conjunto sem uma conversão de string. Aqui está um artigo que descreve alguns dos recursos e a documentação do MDN no objeto Set.
Agora, eu escrevi um polyfill para o objeto definido do ES6 para que você possa começar a usá-lo agora e ele será automaticamente adiado para o objeto definido interno, se o navegador suportar. Isso tem a vantagem de você escrever um código compatível com ES6 que funcionará desde o IE7. Mas existem algumas desvantagens. A interface do conjunto ES6 tira proveito dos iteradores do ES6 para que você possa fazer coisas como
for (item of mySet)
ele irá automaticamente iterar o conjunto para você. Mas, esse tipo de recurso de idioma não pode ser implementado via polyfill. Você ainda pode iterar um conjunto ES6 sem usar os novos recursos de idiomas do ES6, mas, francamente, sem os novos recursos de idioma, não é tão conveniente quanto a outra interface de conjunto que incluo abaixo.Você pode decidir qual funciona melhor para você depois de analisar os dois. O polyfill do conjunto ES6 está aqui: https://github.com/jfriend00/ES6-Set .
Para sua informação, em meus próprios testes, notei que a implementação do Firefox v29 Set não está totalmente atualizada com o rascunho atual da especificação. Por exemplo, você não pode encadear
.add()
chamadas de método como a especificação descreve e meu polyfill é compatível. Provavelmente é uma questão de especificação em movimento, pois ainda não está finalizada.Objetos de conjunto pré- criado : se você deseja um objeto já criado que possui métodos para operar em um conjunto que pode ser usado em qualquer navegador, pode usar uma série de objetos pré-criados diferentes que implementam diferentes tipos de conjuntos. Existe um miniSet que é um código pequeno que implementa o básico de um objeto definido. Ele também possui um objeto de conjunto mais rico em recursos e várias derivações, incluindo um Dicionário (vamos armazenar / recuperar um valor para cada chave) e um ObjectSet (vamos manter um conjunto de objetos - objetos JS ou objetos DOM, onde você fornece o função que gera uma chave exclusiva para cada um ou o ObjectSet irá gerar a chave para você).
Aqui está uma cópia do código para o miniSet (o código mais atualizado está aqui no github ).
fonte
Object.keys(obj)
.Object.keys()
precisa do IE9, FF4, Safari 5, Opera 12 ou superior. Há um polyfill para navegadores mais antigos aqui .obj.hasOwnProperty(prop)
para verificações de associação. Use emObject.prototype.hasOwnProperty.call(obj, prop)
vez disso, que funciona mesmo que o "conjunto" contenha o valor"hasOwnProperty"
.Você pode criar um objeto sem propriedades como
que pode atuar como um conjunto e elimina a necessidade de uso
hasOwnProperty
.fonte
set = {}
lo, herdará todas as propriedades de Object (por exemplotoString
), portanto precisará verificar a carga útil do conjunto (propriedades que você adicionou) comhasOwnProperty
inif (A in set)
set[A]=true
instruções para cada elemento que deseja adicionar em vez de apenas um inicializador?s = Object.create(null);s["thorben"] = true;ss = Object.create(s)
No ECMAScript 6, a estrutura de dados do conjunto é um recurso interno . A compatibilidade com as versões do node.js. pode ser encontrada aqui .
fonte
in
não funciona porque osSet
objetos não têm seus elementos como propriedades, o que seria ruim porque os conjuntos podem ter elementos de qualquer tipo, mas propriedades são cadeias de caracteres. Você pode usarhas
:Set([1,2]).has(1)
Na versão ES6 do Javascript, você incorporou o tipo de conjunto ( verifique a compatibilidade com seu navegador ).
Para adicionar um elemento ao conjunto, você simplesmente usa
.add()
, que é executadoO(1)
e adiciona o elemento ao conjunto (se ele não existir) ou não faz nada se já estiver lá. Você pode adicionar elemento de qualquer tipo lá (matrizes, strings, números)Para verificar o número de elementos no conjunto, você pode simplesmente usar
.size
. Também é executado emO(1)
Para remover o elemento do conjunto, use
.delete()
. Retorna true se o valor estava lá (e foi removido) e false se o valor não existir. Também é executadoO(1)
.Para verificar se o elemento existe em um conjunto
.has()
, use true, que retorna true se o elemento estiver no conjunto e false, caso contrário. Também é executadoO(1)
.Além dos métodos desejados, existem alguns adicionais:
numbers.clear();
apenas removeria todos os elementos do conjuntonumbers.forEach(callback);
iterando pelos valores do conjunto na ordem de inserçãonumbers.entries();
crie um iterador de todos os valoresnumbers.keys();
retorna as teclas do conjunto, que é o mesmo quenumbers.values()
Há também um conjunto de armas que permite adicionar apenas valores de tipo de objeto.
fonte
.add()
execuções em O (1)? Estou intrigado com isso,Eu iniciei uma implementação de Sets que atualmente funciona muito bem com números e strings. Meu foco principal era a operação de diferença, então tentei torná-la o mais eficiente possível. Garfos e revisões de código são bem-vindos!
https://github.com/mcrisc/SetJS
fonte
Acabei de notar que a biblioteca d3.js. possui implementação de conjuntos, mapas e outras estruturas de dados. Não posso discutir sobre a eficiência deles, mas, a julgar pelo fato de ser uma biblioteca popular, deve ser o que você precisa.
A documentação está aqui
Por conveniência, copio do link (as 3 primeiras funções são de interesse)
Constrói um novo conjunto. Se matriz for especificada, adiciona a matriz especificada de valores de sequência ao conjunto retornado.
Retorna true se e somente se este conjunto tiver uma entrada para a cadeia de valor especificada.
Adiciona a cadeia de valor especificada a este conjunto.
Se o conjunto contiver a cadeia de valor especificada, a remove e retorna true. Caso contrário, esse método não fará nada e retorna falso.
Retorna uma matriz dos valores da sequência neste conjunto. A ordem dos valores retornados é arbitrária. Pode ser usado como uma maneira conveniente de calcular os valores exclusivos para um conjunto de strings. Por exemplo:
d3.set (["foo", "bar", "foo", "baz"]). values (); // "foo", "bar", "baz"
Chama a função especificada para cada valor neste conjunto, passando o valor como argumento. O contexto da função é este conjunto. Retorna indefinido. A ordem da iteração é arbitrária.
Retorna true se e somente se este conjunto tiver valores zero.
Retorna o número de valores neste conjunto.
fonte
Sim, é uma maneira sensata - é tudo o que um objeto é (bem, para este caso de uso) - um monte de chaves / valores com acesso direto.
Você precisaria verificar se ele já está lá antes de adicioná-lo, ou se você só precisa indicar presença, "adicioná-lo" novamente não muda nada, apenas o define novamente no objeto.
fonte