web-dev-qa-db-fra.com

Comment parcourir les clés et les valeurs de la carte?

J'ai la carte suivante que je veux parcourir: 

(def db {:classname "com.mysql.jdbc.Driver" 
         :subprotocol "mysql" 
         :subname "//100.100.100.100:3306/clo" 
         :username "usr" :password "pwd"})

J'ai essayé ce qui suit, mais plutôt que d'imprimer la clé et la valeur une fois , il imprime à plusieurs reprises la clé et les valeurs sous diverses combinaisons:

(doseq [k (keys db) 
        v (vals db)] 
  (println (str k " " v)))

J'ai proposé une solution, mais celle de Brian (voir ci-dessous) est beaucoup plus logique.

(let [k (keys db) v (vals db)] 
  (do (println (apply str (interpose " " (interleave k v))))))
57
sjac

C'est le comportement attendu. (doseq [x ... y ...]) va parcourir chaque élément de y pour chaque élément de x.

Au lieu de cela, vous devriez parcourir la carte elle-même une fois. (seq some-map) renverra une liste de vecteurs à deux éléments, un pour chaque paire clé/valeur de la carte. (En réalité, ils sont clojure.lang.MapEntry, mais se comportent comme des vecteurs à 2 éléments.) 

user> (seq {:foo 1 :bar 2})
([:foo 1] [:bar 2])

doseq peut parcourir cette période comme une autre. Comme la plupart des fonctions de Clojure qui fonctionnent avec des collections, doseq appelle en interne seq sur votre collection avant de l'itérer. Donc, vous pouvez simplement faire ceci:

user> (doseq [keyval db] (prn keyval))
[:subprotocol "mysql"]
[:username "usr"]
[:classname "com.mysql.jdbc.Driver"]
[:subname "//100.100.100.100:3306/clo"]
[:password "pwd"]

Vous pouvez utiliser key et val, ou first et second, ou nth, ou get pour extraire les clés et les valeurs de ces vecteurs.

user> (doseq [keyval db] (prn (key keyval) (val keyval)))
:subprotocol "mysql"
:username "usr"
:classname "com.mysql.jdbc.Driver"
:subname "//100.100.100.100:3306/clo"
:password "pwd"

De manière plus concise, vous pouvez utiliser la déstructuration pour lier chaque moitié des entrées de la carte à des noms utilisables dans le formulaire doseq. C'est idiomatique:

user> (doseq [[k v] db] (prn k v))
:subprotocol "mysql"
:username "usr"
:classname "com.mysql.jdbc.Driver"
:subname "//100.100.100.100:3306/clo"
:password "pwd"
89
Brian Carper

Vous pouvez simplement faire

(map (fn [[k v]] (prn k) (prn v)) {:a 1 :b 2})

Le résultat est:

:a
1
:b
2

Est ce que c'est ce que vous recherchiez?

14
Mrinal Saurabh

Juste un petit ajout à la réponse de Brian:

Votre version originale pourrait également être écrite comme suit.

(doseq [[k v] (map vector (keys db) (vals db))]
  (println (str k " " v)))

Dans ce cas, c'est évidemment idiot. Mais en général, cela fonctionne également pour les séquences d'entrée non liées, qui ne proviennent pas de la même carte.

4
kotarak

Ce n'est pas tout à fait clair si vous essayez de résoudre quelque chose au-delà de la simple impression de valeurs (effets secondaires), et si c'est tout ce que vous cherchez, je pense que la solution doseq ci-dessus serait la plus idiomatique. Si vous souhaitez effectuer certaines opérations sur les clés et les valeurs de la carte et renvoyer le même type de structure de données, consultez plutôt reduce-kv, pour lequel vous pouvez trouver les docs pour ici

Comme reduce, reduce-kv accepte une fonction, une valeur de départ/un accumulateur et certaines données, mais dans ce cas, les données sont une carte au lieu d'une séquence. La fonction obtient trois arguments: l'accumulateur, la clé actuelle et la valeur actuelle. Si vous souhaitez effectuer certaines transformations de données et les restituer, cela me semble être le bon outil.

0
rdgd