En Clojure, ¿hay una manera fácil de convertir entre tipos de listas?

92

A menudo me encuentro usando una lista perezosa cuando quiero un vector, y viceversa. Además, a veces tengo un vector de mapas, cuando realmente quería un conjunto de mapas. ¿Hay alguna función auxiliar que me ayude a convertir entre estos tipos?

appshare.co
fuente

Respuestas:

146

No olvidemos que trusty old le intopermite tomar cualquier cosa que seqpueda (lista, vector, mapa, conjunto, mapa ordenado) y un contenedor vacío que desee llenar, y lo coloca into.

(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"

intoes un envoltorio conj, que es la abstracción básica para insertar nuevas entradas de manera adecuada en una colección según el tipo de colección. El principio que hace que esto fluya tan bien es que Clojure se basa en abstracciones componibles , en este caso intosobre la conjcolección y seq.

Los ejemplos anteriores aún se compondrían bien si el destinatario se pasara en tiempo de ejecución: porque las abstracciones subyacentes ( seqy conj) se implementan para todas las colecciones (y muchas de las colecciones de Java también), por lo que las abstracciones más altas no necesitan preocuparse. sobre muchos casos especiales relacionados con los datos.

Arthur Ulfeldt
fuente
3
+1 para en ... vale la pena señalar que también funciona con contenedores originales no vacíos (es decir, cuando desea agregar a una colección)
mikera
11
También vale la pena señalar que debido a que los intousos conj, el hacer (into '() some-seq)producirán una lista que es la inversa de some-seq, porque se conjconvierte en listas.
Chuck
Vale la pena señalar que intoutiliza transitorios (para la mayoría de los tipos de secuencia) para obtener mejores características de rendimiento que la mayoría de los otros medios de conversión.
Jarred Humphrey
Y ahora funciona con transductores, que no existían en el momento en que se escribió esta respuesta (no sé si los transitorios tampoco lo hicieron) (Esta respuesta tiene la edad suficiente para inscribirse en el jardín de infantes)
Arthur Ulfeldt
33

vecsety , en general, intoson tus amigos para "convertir" fácilmente a otro tipo de colección.

¿Cómo quiere transformar un vector de mapas en un mapa de mapas? Necesita una clave, ¿puede proporcionar uso con entrada de muestra / salida esperada?

cgrand
fuente
Lo siento, me refiero a un conjunto de mapas ... He modificado la pregunta ahora
appshare.co
22

Para los vectores existe la vecfunción

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

Para las secuencias perezosas existe la lazy-seqfunción

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

Para convertir en conjuntos, existe la setfunción

user=> (set [{:a :b, :c :d} {:a :b} {:a :b}])
#{{:a :b} {:a :b, :c :d}}
cobbal
fuente
4
Cuando tiene algo que no es perezoso llamando en lazy-seqlugar de seqsimplemente agrega una indirección inútil. Si realmente desea devolver algo que no sea nulo, incluso antes de que exista una colección vacía sequence. lazy-seqes algo así como una construcción de bajo nivel.
cgrand
14

Una respuesta más para convertir de una lista a un mapa (en aras de la integridad), desde aquí :

(apply hash-map '(1 2 3 4))
;=>{1 2, 3 4}
ojo de halcón
fuente
9

Para convertir un vector en una lista también puede usar for, así:

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

Cuando no desee manipular los datos, simplemente use seqen el vector:

=> (seq [1 2 3])
(1 2 3)
Choza Geert-Jan
fuente
En lugar de forhacerlo, simplemente podría hacerlo(map identity [1 2 3 4])
siltalau
7

No es necesario convertir un vector en una lista. Clojure tratará un vector como trataría una lista, como una secuencia, cuando se requiera una secuencia. Por ejemplo,

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

Si necesita asegurarse de que el vector se esté tratando como una secuencia, envuélvalo en 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)

Si tiene un vector de mapas y desea un conjunto de mapas, no importa que el vector contenga mapas. Simplemente convierta el vector en un conjunto como de costumbre:

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