【问题标题】:How to create a map from a list of key-value pairs using values as a predicate in Clojure?如何在 Clojure 中使用值作为谓词从键值对列表创建映射?
【发布时间】:2014-11-10 06:23:08
【问题描述】:

刚开始学习 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}}

into 不接受谓词,因此最后一个获胜。我还尝试了 :letmerge-with,但似乎无法正确引用地图、消除不需要的对或在处理时替换地图的值。

【问题讨论】:

    标签: clojure


    【解决方案1】:

    您可以通过将一系列序列转换串联在一起来做到这一点。

    (->> 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】:

    当您只需要获取一系列值并且没有额外的转换构造仅使用 conj 的结果时,Into 往往最有用。执行构造的其他任何事情往往更适合通过预处理(例如排序)或通过减少来执行累加器内省,例如您想要的。

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

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

    现在我们可以编写一个转换,将累加器中的当前值与“下一个”值进行比较并保持最大值。 -> 有点无偿使用以使更新功能更具可读性。

    (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
    

    【讨论】:

    • 不知道为什么在这里需要字符串比较器。 OP 的数据需要比较原始映射中的 value 元素,它是一个整数。 IE。 [0 1 "a"] = 2, [0 1 "b"] = 1. 2 > 1 所以保留第一个键并转换为 {[0 1] "a"}
    • 谢谢,这给了我一些想法。但是,您正在丢弃原始值并将 z 与 v 进行比较,这不是我的意图。相反,我认为您必须将早期集合 zv 保留在某处,然后将其与新 v 进行比较z 进行测试。
    【解决方案3】:
    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"}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-13
      • 2021-05-06
      • 2011-10-04
      相关资源
      最近更新 更多