【问题标题】:How to find the match from list of maps with list of keys in clojure如何从具有 clojure 中键列表的映射列表中找到匹配项
【发布时间】:2021-08-31 22:41:55
【问题描述】:

我是 Clojure 的新手。

我有一个地图 s1 和一个向量 s2

(def s1 {:params [{:param  :begin_date
                 :name     "begin date"
                 :dtk      :date
                 :param_operand :single}
                {:param  :begin_num
                 :name     "begin num"
                 :dtk      :numeric
                 :param_operand :single}
                {:param  :begin_num2
                 :name     "begin num2"
                 :dtk      :numeric
                 :biparam_operand :single}]})

(def s2 [:begin_date :begin_num]) 

我正在尝试从 s1 和 s2 中找到匹配项。 IE 遍历 s2 中的每个元素并从 s1 中找到匹配项。 如果匹配存在,请检查 :dtk 的类型,如果类型是 :date 则我们接受该映射。

这是我尝试过的代码,当我必须使用列表 s2 中的一个元素来查找匹配是否存在时 - :begin_date

(defn test->map
  [s1 s2]
  (let [test-vec (->> s1
                      :params
                      (filter (fn [typet] (= (:param typet) :begin_date)))
                      first
                      :dtk)]
    (when (= test-vec :date)
      "done")))

如何修改上述函数,使其能够遍历整个元素列表并找到匹配项。 TIA。

【问题讨论】:

标签: clojure


【解决方案1】:

一种使用keep的方法:

(defn match [k]
  (->> s1
       :params
       (keep
        (fn [{:keys [param] :as v}]
          (when (= param k)
            v)))
       first))

或将for:when 一起使用:

(defn lookup [k]
  (first
   (for [{:keys [param] :as v} (:params s1)
         :when (= param k)]
     v)))

(这有点类似于您使用forfilter 的方法。现在只有:when 进行过滤,我返回实际值而不是字符串"done"。)

结果:

(map lookup s2)
;; => ({:param :begin_date, :name "begin date", :dtk :date, :param_operand :single}
;;     {:param :begin_num, :name "begin num", :dtk :numeric, :param_operand :single})

我会亲自将当前数据结构s1 转换为Clojure 映射(例如通过reduce),因此您可以通过键访问参数并使用select-keys。这样您就不必每次查找都遍历整个 s2。

(defn transform [s1]
  (reduce
   (fn [acc {:keys [param] :as v}]
     (assoc acc param v))
   {}
   (:params s1)))

(transform s1)
;; => {:begin_date
;;     {:param :begin_date, :name "begin date", :dtk :date, :param_operand :single},
;;     :begin_num
;;     {:param :begin_num, :name "begin num", :dtk :numeric, :param_operand :single},
;;     :begin_num2
;;     {:param :begin_num2,
;;      :name "begin num2",
;;      :dtk :numeric,
;;      :biparam_operand :single}}

(select-keys (transform s1) s2)
;; => {:begin_date
;;     {:param :begin_date, :name "begin date", :dtk :date, :param_operand :single},
;;     :begin_num
;;     {:param :begin_num, :name "begin num", :dtk :numeric, :param_operand :single}}

【讨论】:

  • 感谢您的回复@Erwin。这个解决方案有帮助。但是,如果我还想过滤结果映射以查找 :dtk 类型并接受 :dtk :date 怎么办。
  • @Sourab 例如,预过滤:(filter (comp #{:date} :dtk) (:params s1))。在使用first 的方法时,您可能会错过条目。
  • 我的函数也应该接受两个参数 s1 和 s2。尝试通过两个参数和过滤器。没运气。我在这里错过了什么吗?
  • @Sourab 要传递两个参数,您必须更改签名,例如(defn lookup [s1 k] ...),并将额外的参数传递给需要它的函数。例如,要将s1 传递给新的lookup,您可以使用partial(filter (partial lookup s1) s2)
  • 以下代码可以正常工作(defn lookup [s1 s2] (first (for [{:keys [param] :as v} (filter (comp #{:date} :dtk) (:params s1)) :when (= param s2)] v))) 感谢您的帮助。我很感激。
猜你喜欢
  • 2018-11-14
  • 1970-01-01
  • 2021-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多