在 Clojure 中对地图向量进行排序和排序的更简洁方法?

Cleaner Way to Sort and Order a Vector of Maps in Clojure?

我有一个地图向量,其中我需要删除名称键值重复的地图,保留年龄值最高的地图。我有一个解决方案,但我认为它看起来不干净。有没有更好的方法来做到这一点而不将其分解为多个功能?

这是我的数据:

(def my-maps
    [{:name "jess", :age 32} 
     {:name "ruxpin", :age 4} 
     {:name "jess", :age 35} 
     {:name "aero", :age 33} 
     {:name "banner", :age 4}])

这是我的解决方案:

(map first (vals (group-by :name (reverse (sort-by :name my-maps)))))

结果:

({:name "ruxpin", :age 4} {:name "jess", :age 35} {:name "banner", :age 4} {:name "aero", :age 33})

不幸的是,您原来的解决方案实际上被破坏了。它似乎有效是因为您在 my-set 中的数据顺序。请注意您实际上从未按年龄排序,因此您永远无法保证年龄的顺序。

我通过再次调用 map 解决了这个问题:

(->> my-set (group-by :name) 
            (vals)

            ; Sort by age each list that group-by returns
            (map #(sort-by :age %)) 

            (map last)) ; This could also happen in the above map

请注意我是如何按 :age 对每个 :name 组进行排序的,然后 我取每个分组的最后一个。

简短版本

(->> my-set
     (sort-by (juxt :name :age) #(compare %2 %1)) ; sort-by :name, :age in reverse order
     (partition-by :name)
     (map first))

换能器版本

(def xf (comp (partition-by :name) (map first)))
(->> my-set
     (sort-by (juxt :name :age) #(compare %2 %1))
     (into [] xf))

对于大数据集,换能器应该更好

我会做一些不同的事情,使用 max 函数而不是排序:

(def my-maps
  [{:name "jess", :age 32}
   {:name "ruxpin", :age 4}
   {:name "jess", :age 35}
   {:name "aero", :age 33}
   {:name "banner", :age 4}])

(dotest
  (let [grouped-data  (group-by :name my-maps)
        name-age-maps (for [[name map-list] grouped-data]
                        (let [max-age      (apply max
                                             (map :age map-list))
                              name-age-map {name max-age}]
                          name-age-map))
        final-result  (reduce into {} name-age-maps)]
    final-result))

结果:

grouped-data => 
{"jess" [{:name "jess", :age 32} {:name "jess", :age 35}],
 "ruxpin" [{:name "ruxpin", :age 4}],
 "aero" [{:name "aero", :age 33}],
 "banner" [{:name "banner", :age 4}]}

name-age-maps => 
({"jess" 35} {"ruxpin" 4} {"aero" 33} {"banner" 4})

final-result => 
{"jess" 35, "ruxpin" 4, "aero" 33, "banner" 4}

另一种方式是group-bymax-key的组合。这种方法的优点是不需要对你的集合进行排序,sort反过来对性能有影响,如果可以避免的话应该是。

(for [[_ vs] (group-by :name my-maps)]
  (apply max-key :age vs))

;;=> ({:name "jess", :age 35} 
;;    {:name "ruxpin", :age 4} 
;;    {:name "aero", :age 33} 
;;    {:name "banner", :age 4})

按不同权重和数据类型的向量字段比较(size权重大),size降序,name升序:

(def some-vector [{:name "head" :size 3}
                            {:name "mouth" :size 1}
                            {:name "nose" :size 1}
                            {:name "neck" :size 2}
                            {:name "chest" :size 10}
                            {:name "back" :size 10}
                            {:name "abdomen" :size 6}
                            ])
(->> (some-vector)
  (sort #(compare (str (format "%3d" (:size %2)) (:name %1))
                  (str (format "%3d" (:size %1)) (:name %2))
  )))