用于动态地图创建的Clojure成语

时间:2014-11-14 19:30:14

标签: clojure

我想基于变量输入创建一个地图,其中只有当相应的值不是nil时才能存在一个键。

这是我想出的一个玩具示例:

(defn make-map
  [foo bar baz]
  (-> {}
      (into (and foo {:foo foo}))
      (into (and bar {:bar bar}))
      (into (and baz {:baz baz}))))

有更多接受/惯用的方法吗?

6 个答案:

答案 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不同。但通常情况下,在消费阶段而不是在转化期间进行nilfalse惩罚会更好。

答案 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]))))