关于排序:Clojure:按嵌套地图自定义比较器排序

Clojure: sort-by nested map custom comparator

我有一个嵌套的Clojure地图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  :1 {
    :priority"Medicore"
    :somekey"SomeValue"
  },
  :2 {
    :priority"Enormous"
    :somekey"SomeValue"
  },
  :3 {
    :priority"Weeny"
    :somekey"SomeValue"
  }
}

我的目标是从"最高"优先级的地图中获取somekey的值。 外部映射可以包含0-n个具有三个优先级中任何一个的元素。 如果有多个条目具有最高的可用优先级,那么我可以接受其中任何一个。

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

这可能吗? 我的方法甚至是实现我目标的正确方法吗?


为了使用sort-by,您需要提供顺序或优先级值。 您可以实现一个自定义比较器来比较您的地图,或者为sort-by定义一个keyfn来计算用于排序的键。 keyfn的解决方案如下。 仅使用keyfn返回符合您要求的可比较值比实现比较器要容易得多。 您可能需要看《比较器指南》。

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

1
2
3
4
5
6
7
8
9
10
(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

现在我们需要为每个地图计算最大优先级:

1
2
3
4
5
6
7
8
9
10
(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了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(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"}})