如何在Clojure中迭代Map键和值



我有下面的映射,我想迭代:

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

我已经尝试了以下操作,但它不是打印一次键和值,而是以各种组合方式重复打印键和值:

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

我想出了一个解决方案,但Brian的(见下文)更合乎逻辑。

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

这是预期的行为。(doseq [x ... y ...])将遍历y中的每一项,以获取x中的每一项。

相反,应该对映射本身迭代一次。(seq some-map)将返回一个包含两项向量的列表,一个用于映射中的每个键/值对。(实际上它们是clojure.lang.MapEntry,但表现得像2项向量。)
user> (seq {:foo 1 :bar 2})
([:foo 1] [:bar 2])

doseq可以像迭代其他序列一样迭代该序列。像Clojure中处理集合的大多数函数一样,在迭代集合之前,doseq在内部调用seq。所以你可以简单地这样做:

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

您可以使用keyval,或firstsecond,或nth,或get从这些向量中获取键和值。

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"
更简单地说,您可以使用解构将map条目的每一半绑定到可以在doseq表单中使用的一些名称。这是习惯用法:
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"

你可以直接做

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

结果是:

:a
1
:b
2

这是你要找的吗?

对Brian的回答做一个简短的补充:

您的原始版本也可以写成如下:

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

在这种情况下,这显然是愚蠢的。但一般来说,这也适用于不相关的输入序列,它们不是来自同一个映射。

如果您试图解决的问题不仅仅是打印值(副作用),则不完全清楚,如果这就是您所要做的,那么我认为上面的doseq解决方案将是最惯用的。如果您想对映射的键和值执行一些操作,并返回相同类型的数据结构,那么您应该查看reduce-kv,您可以在这里找到相关文档

reduce一样,reduce-kv也接受一个函数、一个起始值/累加器和一些数据,但是这里的数据是一个映射而不是一个序列。该函数传递三个参数:累加器、当前键和当前值。如果您确实想要进行一些数据转换并返回一些数据,那么对我来说,这似乎是适合这项工作的工具。

最新更新