Clojure:按嵌套映射自定义比较器排序

时间:2017-10-30 08:45:40

标签: sorting clojure

我有一个嵌套的Clojure地图:

{
  :1 {
    :priority "Medicore"
    :somekey "SomeValue"
  },
  :2 {
    :priority "Enormous"
    :somekey "SomeValue"
  },
  :3 {
    :priority "Weeny"
    :somekey "SomeValue"
  }
}

我的目标是从具有“最高”优先级的地图中获取somekey的值。外部地图可以包含0-n个具有三个优先级中任何一个的元素。如果有多个条目具有最高可用优先级,我可以选择任何一个。

在研究了其他一些SO问题之后,我认为解决这个问题的一个好方法是使用sort-by函数。但由于我的priority不是自然排序的,我必须提供一些自定义比较器。

这可能吗?我的方法是否符合我的目标?

1 个答案:

答案 0 :(得分:1)

要使用sort-by,您需要提供订购或优先级值。您可以实现自定义比较器来比较您的地图或定义用于计算用于排序的键的sort-by的keyfn。 keyfn的解决方案如下。仅使用keyfn返回与您的要求相匹配的可比值比实现比较器容易得多。您可能需要查看Comparators Guide

我们开始定义一个函数来将字符串优先级转换为其数字表示形式:

(let [priorities {"Medicore" 0
                  "Enormous" 1
                  "Weeny"    2}]
  (defn priority->num [p]
    (if-let [num (priorities p)]
      num
      (throw (IllegalArgumentException. (str "Unknown priority: " p))))))

(priority->num "Enormous")
;; => 1

现在我们需要计算每张地图的最高优先级:

(defn max-priority-num [m]
  (->> m
       (vals)
       (map (comp priority->num :priority))
       (apply max)))

(max-priority-num {:1 {:priority "Medicore" :somekey "SomeValue"}
                   :2 {:priority "Enormous" :somekey "SomeValue"}
                   :3 {:priority "Weeny"    :somekey "SomeValue"}})
;; => 2

现在我们终于可以使用sort-by

(def m1 {:1 {:priority "Medicore" :somekey "SomeValue"}
         :2 {:priority "Medicore" :somekey "SomeValue"}
         :3 {:priority "Weeny"    :somekey "SomeValue"}})

(def m2 {:1 {:priority "Medicore" :somekey "SomeValue"}
         :2 {:priority "Enormous" :somekey "SomeValue"}
         :3 {:priority "Weeny"    :somekey "SomeValue"}})

(def m3 {:1 {:priority "Medicore" :somekey "SomeValue"}
         :2 {:priority "Medicore" :somekey "SomeValue"}
         :3 {:priority "Medicore" :somekey "SomeValue"}})

(sort-by max-priority-num [m1 m2 m3])
;; =>
({:1 {:priority "Medicore", :somekey "SomeValue"},
  :2 {:priority "Medicore", :somekey "SomeValue"},
  :3 {:priority "Medicore", :somekey "SomeValue"}}
 {:1 {:priority "Medicore", :somekey "SomeValue"},
  :2 {:priority "Medicore", :somekey "SomeValue"},
  :3 {:priority "Weeny", :somekey "SomeValue"}}
 {:1 {:priority "Medicore", :somekey "SomeValue"},
  :2 {:priority "Enormous", :somekey "SomeValue"},
  :3 {:priority "Weeny", :somekey "SomeValue"}})