Dans Clojure, existe-t-il un moyen simple de convertir entre les types de liste?

92

Je me retrouve souvent à utiliser une liste paresseuse lorsque je veux un vecteur, et vice versa. Aussi, parfois j'ai un vecteur de cartes, alors que je voulais vraiment un ensemble de cartes. Existe-t-il des fonctions d'assistance pour m'aider à convertir entre ces types?

appshare.co
la source

Réponses:

145

N'oublions pas que trusty old intovous permet de prendre tout ce qui est seqcapable (liste, vecteur, carte, ensemble, carte triée) et un conteneur vide que vous voulez remplir, et le met 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"

intoest un wrapper autour conj, qui est l'abstraction de base pour insérer de nouvelles entrées de manière appropriée dans une collection en fonction du type de la collection. Le principe qui fait de ce flux est donc bien que Clojure est construit sur des abstractions composables , dans ce cas , intoau - dessus de conjau - dessus de la collecte et seq.

Les exemples ci-dessus seraient toujours bien composés si le destinataire était passé au moment de l'exécution: parce que les abstractions sous-jacentes ( seqet conj) sont implémentées pour toutes les collections (et de nombreuses collections Java également), donc les abstractions supérieures n'ont pas à s'inquiéter à propos de nombreux cas particuliers liés aux données.

Arthur Ulfeldt
la source
3
+1 pour into ... à noter que cela fonctionne également avec les conteneurs d'origine non vides (c'est-à-dire lorsque vous souhaitez ajouter à une collection)
mikera
11
Il est également intéressant de noter que parce que intouses conj, faire (into '() some-seq)donnera une liste qui est l' inverse de some-seq, parce que conjconsés sur des listes.
Chuck
Il est à noter qu'il intoutilise des transitoires (pour la plupart des types de seq) pour de meilleures caractéristiques de performance que la plupart des autres moyens de conversion.
Jarred Humphrey
Et cela fonctionne maintenant avec des transducteurs, qui n'existaient pas au moment où cette réponse a été écrite (je ne sais pas si les transitoires non plus) (Cette réponse est assez vieille pour s'inscrire à la maternelle)
Arthur Ulfeldt
33

vec, setet intosont généralement vos amis pour "convertir" facilement en un autre type de collection.

Comment voulez-vous transformer un vecteur de cartes en une carte de cartes? Vous avez besoin d'une clé, pouvez-vous fournir une utilisation avec un exemple d'entrée / sortie attendue?

cgrand
la source
Désolé, je voulais dire un ensemble de cartes. J'ai modifié la question maintenant
appshare.co
22

Pour les vecteurs, il y a la vecfonction

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

Pour les séquences paresseuses, il y a la lazy-seqfonction

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

Pour convertir en ensembles, il y a la setfonction

user=> (set [{:a :b, :c :d} {:a :b} {:a :b}])
#{{:a :b} {:a :b, :c :d}}
cobbal
la source
4
Lorsque vous avez quelque chose d'appels non paresseux lazy-seqau lieu d' seqajouter simplement une indirection inutile. Si vraiment vous voulez retourner quelque chose de non nul, même avant une collection vide, alors il y a sequence. lazy-seqest en quelque sorte une construction de bas niveau.
cgrand
14

Une autre réponse pour la conversion d'une liste à une carte (par souci d'exhaustivité) - à partir d' ici :

(apply hash-map '(1 2 3 4))
;=>{1 2, 3 4}
oeil de faucon
la source
9

Pour convertir un vecteur en liste, vous pouvez également utiliser for, comme ceci:

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

Lorsque vous ne souhaitez pas manipuler les données, utilisez simplement seqsur le vecteur:

=> (seq [1 2 3])
(1 2 3)
Cabane Geert-Jan
la source
Au lieu de forvous pourriez juste faire(map identity [1 2 3 4])
siltalau
7

Il n'est pas nécessaire de convertir un vecteur en liste. Clojure traitera un vecteur comme il traiterait une liste - comme une séquence - lorsqu'une séquence est requise. Par exemple,

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

Si vous devez vous assurer que le vecteur est traité comme une séquence, enveloppez-le dans 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 vous avez un vecteur de cartes et que vous voulez un ensemble de cartes, peu importe que le vecteur contienne des cartes. Vous convertissez simplement le vecteur en un ensemble comme d'habitude:

user=> (set [{:a 1, :b 2} {"three" 3, "four" 4}])
#{{:a 1, :b 2} {"four" 4, "three" 3}}
La vignette
la source