如何使用值作为Clojure中的谓词从键值对列表创建映射?

时间:2014-11-10 06:23:08

标签: clojure

刚开始学习Clojure,所以我想我的主要问题是我不知道如何正确地制定问题以找到现有的解决方案。我有一张地图:

{[0 1 "a"] 2, [0 1 "b"] 1, [1 1 "a"] 1}

我希望将其“转换”为:

{[0 1] "a", [1 1] "a"}

即。使用复合键的两个第一个元素作为新键,将第三个元素用作原始映射中具有最高值的键值对的值。

我可以轻松创建新的地图结构:

=> (into {} (for [[[x y z] v] {[0 1 "a"] 2, [0 1 "b"] 1, [1 1 "a"] 1}] [[x y] {z v}]))
{[0 1] {"b" 1}, [1 1] {"a" 1}}

进入不接受任何谓词,所以最后一个获胜。我还尝试了:让合并,但似乎无法正确引用地图,消除不需要的对或在处理时替换地图的值。< / p>

3 个答案:

答案 0 :(得分:0)

当你只需要获取一系列值并且没有额外的转换来构造一个仅使用conj的结果时,它往往是最有用的。你正在进行构造的任何其他东西往往更适合通过预处理(例如排序)或通过缩减来允许你执行累积器内省,如你想要的那样。

首先,我们必须能够比较两个字符串..

(defn greater? [^String a ^String b]
  (> (.compareTo a b) 0))

现在我们可以编写一个转换,将累加器中的当前值与&#34; next&#34;价值并保持最大值。 ->稍微使用了一些,以使更新功能更具可读性。

(defn transform [input]
  (-> (fn [acc [[x y z] _]]      ;; take the acc, [k, v], destructure k discard v
        (let [key [x y]]         ;; construct key into accumulator
          (if-let [v (acc key)]  ;; if the key is set
            (if (greater? z v)   ;;   and z (the new val) is greater
              (assoc acc key z)  ;;   then update
              acc)               ;;   else do nothing
            (assoc acc key z)))) ;; else update
      (reduce {} input)))        ;; do that over all [k, v]s from empty acc

答案 1 :(得分:0)

您可以通过将一系列序列转换线程化来完成此操作。

(->> data
     (group-by #(->> % key (take 2)))
     vals 
     (map (comp first first (partial sort-by (comp - val))))
     (map (juxt #(subvec % 0 2) #(% 2)))
     (into {}))

;{[0 1] "a", [1 1] "a"}

......其中

(def data {[0 1 "a"] 2, [0 1 "b"] 1, [1 1 "a"] 1})

逐行构建解决方案。我建议你跟随建筑的脚步,从......开始。

(->> data
     (group-by #(->> % key (take 2)))

;{(0 1) [[[0 1 "a"] 2] [[0 1 "b"] 1]], (1 1) [[[1 1 "a"] 1]]}

堆叠(懒惰)序列的层可以运行得相当慢,但Clojure 1.7中提供的换能器将允许您在这个习语中编写更快的代码,如this excellent answer中所示。

答案 2 :(得分:0)

user> (def m {[0 1 "a"] 2, [0 1 "b"] 1, [1 1 "a"] 1})
#'user/m
user> (->> m
           keys
           sort
           reverse
           (mapcat (fn [x]
                     (vector (-> x butlast vec)
                             (last x))))
           (apply sorted-map))

;=> {[0 1] "a", [1 1] "a"}