我想基于变量输入创建一个地图,其中只有当相应的值不是nil
时才能存在一个键。
这是我想出的一个玩具示例:
(defn make-map
[foo bar baz]
(-> {}
(into (and foo {:foo foo}))
(into (and bar {:bar bar}))
(into (and baz {:baz baz}))))
有更多接受/惯用的方法吗?
答案 0 :(得分:3)
我觉得这样的事情有点简单
(defn make-map
[foo bar baz]
(reduce (fn [m [k v]] (if (some? v) (assoc m k v) m))
{}
{:foo foo :bar bar :baz baz}))
user> (make-map 1 nil 2)
{:baz 2, :foo 1}
user> (make-map nil 1 2)
{:baz 2, :bar 1}
user> (make-map true false true)
{:baz true, :bar false, :foo true}
答案 1 :(得分:1)
这使用cond->来简化一些事情。
(defn make-map
[foo bar baz]
(cond-> {}
foo (assoc :foo foo)
bar (assoc :bar bar)
baz (assoc :baz baz)))
很难用玩具示例来判断是否有更好的选择。
答案 2 :(得分:1)
你可以抽象它来使用类似于zipmap的语法和应用程序,这样你就可以拥有键和args的变量参数列表
(defn when-zip
[keys args]
(->> args
(map vector keys)
(remove (comp not second))
(into {})))
(when-zip [:foo :bar :baz :qux] [true nil false 1])
=> {:qux 1, :foo true}
当你不喜欢创建中间懒惰结果时,可以使用Clojure 1.7的传感器或公然撕掉zipmap
的来源
(defn when-zip
"Returns a map with each of the keys mapped to
the corresponding val when val is truthy."
[keys vals]
(loop [map {}
ks (seq keys)
vs (seq vals)]
(if (and ks vs)
(recur (if-let [v (first vs)]
(assoc map (first ks) v)
map)
(next ks)
(next vs))
map)))
(when-zip [:foo :bar :baz :qux] [true nil false 1])
=> {:qux 1, :foo true}
如果您确实仍需要原始语法,则可以使用此语法定义特定版本
(defn make-map
[& args]
(when-zip [:foo :bar :baz :qux] args))
(make-map true nil false 1)
=> {:qux 1, :foo true}
另一方面,你可以不用去除nils并使用zipmap
;当你在一个不存在的键上进行地图查找时,它将给出与值为零的键相同的结果:
(:baz {:qux 1, :foo true})
=> nil
(:baz {:qux 1, :baz nil, :bar false :foo true})
=> nil
当然,这与:bar
不同。但通常情况下,在消费阶段而不是在转化期间进行nil
和false
惩罚会更好。
答案 3 :(得分:1)
对于一点点变化,使用for
进行概括:
(defn some-map
[& args]
(->> (for [[k v] (partition 2 args)
:when (some? v)]
[k v])
(into {})))
用法:
(some-map :a 1 :b 2 :c nil :d false)
;; => {:a 1, :b 2, :d false}
或者,类似于@noisesmith的答案,可以应用于现有地图:
(defn some-map
[m]
(into {} (filter (comp some? val) m)))
(some-map {:a 1 :b 2 :c nil :d false})
;; => {:b 2, :d false, :a 1}
答案 4 :(得分:1)
(defn make-map [foo bar baz]
(into {}
(filter
#(if-not (nil? (second %)) { (first %) (second %)})
(map vector [ :foo :bar :baz] [for bar baz]))
)
)
答案 5 :(得分:0)
为了完整起见,这里的内容更接近于我原本想要达到的目标,但却没有完全达到。
(defn make-map
[foo bar baz]
(apply hash-map
(concat
(and foo [:foo foo])
(and bar [:bar bar])
(and baz [:baz baz]))))