Em Clojure, existe uma maneira fácil de converter entre tipos de lista?

92

Freqüentemente, estou usando uma lista preguiçosa quando quero um vetor e vice-versa. Além disso, às vezes tenho um vetor de mapas, quando na verdade queria um conjunto de mapas. Existe alguma função auxiliar para me ajudar a converter entre esses tipos?

appshare.co
fonte

Respostas:

144

Não vamos esquecer que o trusty old intopermite que você pegue qualquer coisa seqcapaz (lista, vetor, mapa, conjunto, mapa classificado) e um recipiente vazio que você deseja preencher e coloque- intoo.

(into [] '(1 2 3 4)) ==> [1 2 3 4]         "have a lazy list and want a vector"
(into #{} [1 2 3 4]) ==> #{1 2 3 4}        "have a vector and want a set"
(into {} #{[1 2] [3 4]}) ==> {3 4, 1 2}    "have a set of vectors want a map"
(into #{} [{1 2} {3 4}]) ==> #{{1 2} {3 4}} "have a vector of maps want a set of maps"

intoé um invólucro conj, que é a abstração base para inserir novas entradas apropriadamente em uma coleção com base no tipo da coleção. O princípio que faz com que esse fluxo tão bem é que Clojure é construir em abstrações componíveis , neste caso intoem cima conjno topo da coleta e seq.

Os exemplos acima ainda funcionariam bem se o destinatário estivesse sendo passado em tempo de execução: porque as abstrações subjacentes ( seqe conj) são implementadas para todas as coleções (e muitas das coleções de Java também), então as abstrações superiores não precisam se preocupar sobre muitos casos especiais relacionados a dados.

Arthur Ulfeldt
fonte
3
+1 para em ... é importante notar que também funciona com recipientes originais não vazios (ou seja, quando você deseja adicionar a uma coleção)
mikera
11
Também é importante notar que porque intousa conj, fazer (into '() some-seq)gerará uma lista que é o reverso de some-seq, porque conjconses em listas.
Chuck
É importante notar que intoutiliza transientes (para a maioria dos tipos de seq) para melhores características de desempenho do que a maioria dos outros meios de conversão.
Jarred Humphrey
E agora funciona com transdutores, que não existiam no momento em que esta resposta foi escrita (não sei se os transitórios também) (Esta resposta é velha o suficiente para se inscrever no jardim de infância)
Arthur Ulfeldt
33

vec, sete geralmente intosão seus amigos para "converter" facilmente para outro tipo de coleção.

Como você deseja transformar um vetor de mapas em um mapa de mapas? Você precisa de uma chave. Você pode fornecer uma amostra de entrada / saída esperada?

cgrand
fonte
Desculpe, eu quis dizer um conjunto de mapas. Eu
alterei
22

Para vetores, existe a vecfunção

user=> (vec '(1 2 3))
[1 2 3]

Para sequências preguiçosas, existe a lazy-seqfunção

user=> (lazy-seq [1 2 3])
(1 2 3)

Para converter em conjuntos, existe a setfunção

user=> (set [{:a :b, :c :d} {:a :b} {:a :b}])
#{{:a :b} {:a :b, :c :d}}
cobbal
fonte
4
Quando você tem algo não preguiçoso chamando em lazy-seqvez de seqapenas adicionar uma indireção inútil. Se você realmente deseja devolver algo não nulo, mesmo antes de uma coleção vazia, então há sequence. lazy-seqé uma construção de baixo nível.
cgrand
14

Mais uma resposta para a conversão de uma lista em um mapa (para fins de integridade) - a partir daqui :

(apply hash-map '(1 2 3 4))
;=>{1 2, 3 4}
Hawkeye
fonte
9

Para converter um vetor em uma lista, você também pode usar for, desta forma:

=> (for [i [1 2 3 4]] i)
(1 2 3 4)

Quando você não quiser manipular os dados, basta usar seqno vetor:

=> (seq [1 2 3])
(1 2 3)
Cabana Geert-Jan
fonte
Em vez de forvocê pode simplesmente fazer(map identity [1 2 3 4])
siltalau
7

Não há necessidade de converter um vetor em uma lista. Clojure tratará um vetor como trataria uma lista - como uma sequência - quando uma sequência é necessária. Por exemplo,

user=> (cons 0 [1 2 3])
(0 1 2 3)

Se você precisa se certificar de que o vetor está sendo tratado como uma sequência, envolva-o em seq:

user=> (conj [1 2 3] 0) ; treated as a vector
[1 2 3 0]

user=> (conj (seq [1 2 3]) 0) ; treated as a sequence
(0 1 2 3)

Se você tem um vetor de mapas e deseja um conjunto de mapas, não importa se o vetor contém mapas. Você apenas converte o vetor em um conjunto, como de costume:

user=> (set [{:a 1, :b 2} {"three" 3, "four" 4}])
#{{:a 1, :b 2} {"four" 4, "three" 3}}
Miniatura
fonte