Clojure - 在一个哈希映射向量中返回两个键匹配的最低哈希映射

3

我有一个哈希映射的向量,格式类似于以下内容:

[{:a 1 :b 2} {:a 3 :b 4} {:a 1 :b 6} {:a 3 :b 9} {:a 5 :b 1} {:a 6 :b 1}]

我希望筛选出匹配:a值的最低:b值,所以如果两个:a值相同,例如:{:a 1 :b 2}{:a 1 :b 6},它应该返回: {:a 1 :b 2} ,因为2比6更小。
因此,对于上面的向量,我希望得到:
[{:a 1 :b 2} {:a 3 :b 4} {:a 5 :b 1} {:a 6 :b 1}]

我尝试了几个方法,但是卡住了,需要帮助,谢谢。


你尝试了什么?你卡在哪里了? - Shlomi
您可以在ClojureDocs.org网站上找到有关比较器的很好的解释(您所需的通用版本):http://clojuredocs.org/clojure.core/sorted-set-by - Alan Thompson
@Shlomi 我尝试了类似这样的代码:(apply min-key :b (filter #(= (:a %) 3) list)),它通过过滤掉所有 :a = 3 的值来在列表中找到 :b 的最小值,但我需要手动输入 :a 的值。 - Campbell Wray
4个回答

4

你最初的方向是正确的。你只是错过了分组部分:

(def test [{:a 1 :b 2} {:a 3 :b 4} {:a 1 :b 6} {:a 3 :b 9} {:a 5 :b 1} {:a 6 :b 1}])

(defn min-map [m]
  (map (partial apply min-key :b) (vals (group-by :a m))))

(min-map test)
=> ({:a 1, :b 2} {:a 3, :b 4} {:a 5, :b 1} {:a 6, :b 1})

首先,我们按键:a对地图列表进行分组,并提取其值。然后,我们使用min-key按键:b查找每个组中的最小值。


谢谢您的回复,我认为这种方法的性能会比我当前解决方案中两次遍历列表要好。 - Campbell Wray
你可以使用critirium来自行测试性能。我认为这种方法更符合习惯,但最终决定权在你手中! - Shlomi

-1

所以我想了一下,现在有了一个答案(虽然不太好):

(defn contains [a vect]
  (apply min-key :b(filter #(= (:a %) (:a a))vect))
  )

(defn starter []
  (let [tester [{:a 1 :b 2} {:a 3 :b 4} {:a 1 :b 6} {:a 3 :b 9} {:a 5 :b 1} {:a 6 :b 1}]]
    (vec(distinct(map #(contains % tester)tester)))
)
)

感谢大家的帮助,如果您有任何批评或更好的解决方案,请发表评论。


-1

有依赖关系

[tupelo "0.1.68"]

我们可以编写以下代码。我留下了许多spy表达式,以便...
(ns clj.core
  (:use tupelo.core)
  (:require [clojure.core               :as clj]
            [schema.core                :as s]
            [tupelo.types               :as tt]
            [tupelo.schema              :as ts]
  ))

; Prismatic Schema type definitions
(s/set-fn-validation! true)   ; #todo add to Schema docs

(def data [ {:a 1 :b 2} {:a 3 :b 4} {:a 1 :b 6} {:a 3 :b 9} {:a 5 :b 1} {:a 6 :b 1} ] )

(defn accum-smallest-b-entries
  [cum-map-a2b 
      ; A map where both keys and vals are simple 1-entry maps
      ; like:   {:a 1} -> {:b 2}
      ;         {:a 2} -> {:b 9}
   new-a-b-map
      ; next entry, like {:a 1 :b 2}
  ]
  (newline)
  (println "---------------------------------")
  (let [new-a-map   (select-keys new-a-b-map [:a] )  ; like {:a 1}
        _ (spyx new-a-map)
        new-b-map   (select-keys new-a-b-map [:b] )  ; like {:b 2}
        _ (spyx new-b-map)
        curr-b-map  (get cum-map-a2b new-a-map)
        _ (spyx curr-b-map)
        next-b-map  (if (or (nil? curr-b-map)
                            (< (grab :b new-b-map) (grab :b curr-b-map)))
                      new-b-map 
                      curr-b-map)
        _ (spyx next-b-map)
  ]
    (spyx (assoc cum-map-a2b new-a-map next-b-map))))

(def a2b-map (reduce accum-smallest-b-entries {} data))
(spyx a2b-map)

(defn combine-keyvals-from-a2b-map
  [cum-result
      ; final result like:  [ {:a 1 :b 2}
      ;                       {:a 2 :b 9} ]
   a2b-entry
      ; next entry from a2b-map like [ {:a 5} {:b 1} ]
   ]
  (newline)
  (println "combine-keyvals-from-a2b-map")
  (println "---------------------------------")
  (spyx cum-result)
  (spyx a2b-entry)
  (let [combined-ab-map (glue (first a2b-entry) (second a2b-entry)) 
        _ (spyx combined-ab-map)
        new-result      (append cum-result combined-ab-map)
        _ (spyx new-result)
        ]
    new-result))

(def a-and-b-map (reduce combine-keyvals-from-a2b-map [] a2b-map))
(spyx a-and-b-map)

(defn -main [] )

运行代码我们得到:

---------------------------------
new-a-map => {:a 1}
new-b-map => {:b 2}
curr-b-map => nil
next-b-map => {:b 2}
(assoc cum-map-a2b new-a-map next-b-map) => {{:a 1} {:b 2}}

---------------------------------
new-a-map => {:a 3}
new-b-map => {:b 4}
curr-b-map => nil
next-b-map => {:b 4}
(assoc cum-map-a2b new-a-map next-b-map) => {{:a 1} {:b 2}, {:a 3} {:b 4}}

---------------------------------
new-a-map => {:a 1}
new-b-map => {:b 6}
curr-b-map => {:b 2}
next-b-map => {:b 2}
(assoc cum-map-a2b new-a-map next-b-map) => {{:a 1} {:b 2}, {:a 3} {:b 4}}

---------------------------------
new-a-map => {:a 3}
new-b-map => {:b 9}
curr-b-map => {:b 4}
next-b-map => {:b 4}
(assoc cum-map-a2b new-a-map next-b-map) => {{:a 1} {:b 2}, {:a 3} {:b 4}}

---------------------------------
new-a-map => {:a 5}
new-b-map => {:b 1}
curr-b-map => nil
next-b-map => {:b 1}
(assoc cum-map-a2b new-a-map next-b-map) => {{:a 1} {:b 2}, {:a 3} {:b 4}, {:a 5} {:b 1}}

---------------------------------
new-a-map => {:a 6}
new-b-map => {:b 1}
curr-b-map => nil
next-b-map => {:b 1}
(assoc cum-map-a2b new-a-map next-b-map) => {{:a 1} {:b 2}, {:a 3} {:b 4}, {:a 5} {:b 1}, {:a 6} {:b 1}}
a2b-map => {{:a 1} {:b 2}, {:a 3} {:b 4}, {:a 5} {:b 1}, {:a 6} {:b 1}}

combine-keyvals-from-a2b-map
---------------------------------
cum-result => []
a2b-entry => [{:a 1} {:b 2}]
combined-ab-map => {:a 1, :b 2}
new-result => [{:a 1, :b 2}]

combine-keyvals-from-a2b-map
---------------------------------
cum-result => [{:a 1, :b 2}]
a2b-entry => [{:a 3} {:b 4}]
combined-ab-map => {:a 3, :b 4}
new-result => [{:a 1, :b 2} {:a 3, :b 4}]

combine-keyvals-from-a2b-map
---------------------------------
cum-result => [{:a 1, :b 2} {:a 3, :b 4}]
a2b-entry => [{:a 5} {:b 1}]
combined-ab-map => {:a 5, :b 1}
new-result => [{:a 1, :b 2} {:a 3, :b 4} {:a 5, :b 1}]

combine-keyvals-from-a2b-map
---------------------------------
cum-result => [{:a 1, :b 2} {:a 3, :b 4} {:a 5, :b 1}]
a2b-entry => [{:a 6} {:b 1}]
combined-ab-map => {:a 6, :b 1}
new-result => [{:a 1, :b 2} {:a 3, :b 4} {:a 5, :b 1} {:a 6, :b 1}]
a-and-b-map => [{:a 1, :b 2} {:a 3, :b 4} {:a 5, :b 1} {:a 6, :b 1}]

回想起来,如果我们能保证每个输入映射都像 {:a :b} 这样简单,那么它可以更加简化,因为我们可以将其简化为一系列 2D 点,例如 [n m],因为关键字 :a 和 :b 将是冗余的。

-1

这里是一个更好的答案,使用 group-by 函数:

(ns clj.core
  (:use tupelo.core)
  (:require [clojure.core               :as clj]
            [schema.core                :as s]
            [tupelo.types               :as tt]
            [tupelo.schema              :as ts]
  ))

; Prismatic Schema type definitions
(s/set-fn-validation! true)   ; #todo add to Schema docs

(def data [ {:a 1 :b 2} {:a 3 :b 4} {:a 1 :b 6} {:a 3 :b 9} {:a 5 :b 1} {:a 6 :b 1} ] )

(def data-by-a (group-by :a data))
    ; like { 1 [{:a 1, :b 2} {:a 1, :b 6}], 
    ;        3 [{:a 3, :b 4} {:a 3, :b 9}], 
    ;        5 [{:a 5, :b 1}],
    ;        6 [{:a 6, :b 1}] }
(spyx data-by-a)

(defn smallest-map-by-b
  [curr-result  ; like {:a 1, :b 2}
   next-value]  ; like {:a 1, :b 6}
  (if (<  (grab :b curr-result)
          (grab :b next-value))
    curr-result
    next-value))

(defn find-min-b
  "Return the map with the smallest b value"
  [ab-maps] ; like [ {:a 1, :b 2} {:a 1, :b 6} ]
  (reduce smallest-map-by-b 
          (first ab-maps)   ; choose 1st as init guess at result
          ab-maps))

(def result 
  (glue
    (for [entry data-by-a]  ; entry is MapEntry like:  [ 1 [{:a 1, :b 2} {:a 1, :b 6}] ]
      (spyx (find-min-b (val entry)))
      )))
(spyx result)

(defn -main [] )

它产生的结果

data-by-a => {1 [{:a 1, :b 2} {:a 1, :b 6}], 3 [{:a 3, :b 4} {:a 3, :b 9}], 5 [{:a 5, :b 1}], 6 [{:a 6, :b 1}]}
(find-min-b (val entry)) => {:a 1, :b 2}
(find-min-b (val entry)) => {:a 3, :b 4}
(find-min-b (val entry)) => {:a 5, :b 1}
(find-min-b (val entry)) => {:a 6, :b 1}
result => [{:a 1, :b 2} {:a 3, :b 4} {:a 5, :b 1} {:a 6, :b 1}]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接