这是有道理的:
user=> (into {} [[:a 1] [:b 2]])
{:a 1, :b 2}
但为什么会产生错误?
user=> (into {} (partition 2 [:a 1 :b 2]))
ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry clojure.lang.ATransientMap.conj (ATransientMap.java:44)
只是为了确定:
user=> (partition 2 [:a 1 :b 2])
((:a 1) (:b 2))
into
是否存在延迟序列的问题?如果是这样,为什么?
除了解释为什么这不起作用之外,建议将一系列键值对(例如[:a 1 :b 2]
)合并到地图中的方法是什么? (apply conj
似乎也不起作用。)
答案 0 :(得分:4)
您可以apply
将序列assoc
:
(apply assoc {:foo 1} [:a 1 :b 2])
=> {:foo 1, :a 1, :b 2}
是否存在延迟序列的问题?如果是这样,为什么?
不,into
通常与懒惰评估的序列一起使用。这是懒惰的,但每个键/值元组都是一个向量,这就是为什么当into
将对减少到地图时它起作用的原因:
(into {} (map vector (range 3) (repeat :x)))
=> {0 :x, 1 :x, 2 :x}
这不起作用,因为键/值对是列表:
(into {} (map list (range 3) (repeat :x)))
所以区别不是懒惰;这是因为into
在地图上使用reduce
使用conj
,这只适用于 vector 键/值对(或MapEntry
s):
(conj {} [:a 1]) ;; ok
(conj {} (MapEntry. :a 1)) ;; ok
(conj {} '(:a 1)) ;; not ok
更新:assoc
包装器,用于应用注释中建议的空/零序列:
(defn assoc*
([m] m)
([m k v & kvs]
(apply assoc m k v kvs)))
答案 1 :(得分:2)
推荐的方法 - (假设seq arg非空,如OP指出的那样) -
Clojure 1.9.0
user=> (apply assoc {} [:a 1 :b 2])
{:a 1, :b 2}
partition
版本不起作用,因为partition
返回的块是seqs,当conj
'到地图时,这些块不会被视为地图条目和实际的地图条目是。
E.g。 (into {} (map vec) (partition 2 [:a 1 :b 2]))
会起作用,因为这里的对在conj
之前转换为向量。
除非有一些使assoc
方便的特殊情况(例如,如果您有一堆传感器要用于预处理{{1},那么使用into
的方法仍然是可取的。生成的对等。)。
答案 2 :(得分:1)
Clojure将2-vec(例如[:a 1]
)视为等同于MapEntry
,执行相当于"自动类型转换"。我尽量避免这种情况并始终明确。
(first {:a 1}) => <#clojure.lang.MapEntry [:a 1]>
(conj {:a 1} [:b 2]) => <#clojure.lang.PersistentArrayMap {:a 1, :b 2}>
所以我们看到一个MapEntry打印得像一个矢量但是有一个不同的类型(就像一个Clojure seq
打印像list
但有不同的类型)。 seq
将Clojure地图转换为MapEntry序列,first
将我们作为第一个(大多数Clojure函数在任何其他处理之前对任何输入集合调用(seq ...)
)。
请注意,conj
执行逆类型转换,将 向量 [:b 2]
视为MapEntry。但是,conj
无法为list
或seq
执行自动类型转换:
(throws? (conj {:a 1} '(:b 2)))
(throws? (into {:a 1} '(:b 2)))
into
有同样的问题since it is basically just (reduce conj <1st-arg> <2nd-seq>)
。
其他答案已有3种方法可供使用:
(assoc {} :b 2) => {:b 2}
(conj {} [:b 2]) => {:b 2}
(into {} [[:a 1] [:b 2]]) => {:a 1, :b 2}
但是,我会避免这些并坚持hash-map
或sorted-map
,这两者都避免了空输入seqs的问题:
(apply hash-map []) => {} ; works for empty input seq
(apply hash-map [:a 1 :b 2]) => {:b 2, :a 1}
如果输入序列是对的列表,flatten
有时会有用:
(apply sorted-map (flatten [[:a 1] [:b 2]])) => {:a 1, :b 2}
(apply hash-map (flatten '((:a 1) (:b 2)))) => {:a 1, :b 2}
请注意,这些不一样:
如果您已有地图并希望合并(可能为空)键值对序列,请使用into
和hash-map
的组合:
(into {:a 1} (apply hash-map [])) => {:a 1}
(into {:a 1} (apply hash-map [:b 2])) => {:a 1, :b 2}