如何在Clojure中通过多个键过滤地图矢量 [英] How to filter vector of maps by multiple keys in Clojure

查看:69
本文介绍了如何在Clojure中通过多个键过滤地图矢量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们有一个像这样的数据结构:

 (def data 
(atom [{:id 1 :first-name John1:last-name Dow1:age 14}
{:id 2:first-name John2:last-name Dow2:age 54}
{:id 3:名字 John3:姓氏 Dow3:年龄 34}
{:id 4:名字 John4:姓氏 Dow4:年龄 12}
{:id 5:名字 John5:姓氏 Dow5:年龄 24}])))

我了解了如何通过一个键对其进行过滤,例如:

 (defn my-filter 
[str-input]
(filter#(重新查找(->>(str str-input)
(小写)
(重新模式))
(小写(:first%)))
@data))

> (我的过滤器 John1)
> ({:last-name Dow1,:age 14,:first-name John1,:id 1})

但是现在我有点困惑于如何通过:firstname :last-名称:age 的简单方法?


更新:很抱歉没有太清楚了,无法解释问题所在...实际上,我希望所有键:first-name :last-name :age 加入过滤器功能,这样,如果 str-input 不匹配:名字的值,请检查它是否匹配:姓氏的值,依此类推。 / p>

更新2
尝试 some-fn 后,每个pred 换能器,我没有得到所需的东西,例如过滤谓词中的正则表达式,我认为目前尚缺乏知识。
因此,我最终得到了该功能,该功能可以正常工作,但是代码很丑陋且重复。我该如何消除代码重复?

 (defn my-filter [str-input] 
(let [名字(过滤器#(重新查找(->>(str str-input)
(大写)
(重新模式))
(大写(:first -name%)))
@data)
姓氏(过滤器#(重新查找(->>(str str-input)
(大写)
(重新模式))
(大写(:姓氏%)))
@data)
age(过滤器#(重新查找(->>(str str-input)
(大写)
(重新模式))
(大写(:age%)))
@data)]
(如果不是(空?名字)
firstname
(如果不是(空?姓氏)​​
姓氏
(如果不是(空的年龄)
年龄)))))


解决方案

这也可以借助功能组合来实现,例如您可以使用 every-pred 函数,该函数创建一个函数,检查所有pred的参数是否真实,并使用其过滤数据。例如,如果要查找所有奇数值为:id :last-name 为<$ c $的项目c> Dow1, Dow2,或 Dow3 :age \3 开头:

  user> (def data 
[{:id 1:名字 John1:姓氏 Dow1:年龄 14}
{:id 2:名字 John2:last-名称 Dow2:age 54}
{:id 3:first-name John3:姓氏 Dow3:age 34}
{:id 4:first-name John4:姓氏 Dow4:年龄 12}
{:id 5:名字 John5:姓氏 Dow5:年龄 24}]))

位用户> (过滤器(每个pred(comp奇数?:id)
(comp#{ Dow1 Dow2 Dow3}:姓)
(comp#{\3} first:年龄))
数据)

;; => ({:id 3,:first-name John3,:last-name Dow3,:age 34})

另一种方法是使用传感器

  user> (序列(comp(过滤器(comp奇数?:id)))
(过滤器(comp#{ Dow1 Dow2 Dow3}:姓))
数据)

请注意,实际过滤仅对每个项目进行一次,因此不会创建任何中间集合。



更新



根据您的更新,您需要在 any 的谓词为真,因此可以使用 some 函数代替 every-pred

  user> ;: (过滤器#(一些(fn [pred](pred%))
[(compod?:id)
(comp#{ Dow1 Dow2 Dow4}:姓氏)
(comp(partial = \3)first:age)])
数据)
;; => ({:id 1,:first-name John1,:last-name Dow1,:age 14} {:id 2,:first-name John2,:last-name Dow2,:年龄 54} {:id 3,:名字 John3,:姓 Dow3,:age 34} {:id 4,:名字 John4,:last Dow4,:age 12} {:id 5,:first-name John5,:last-name Dow5,:age 24})


Assume we have a datastructure like this one:

(def data
     (atom [{:id 1 :first-name "John1" :last-name "Dow1" :age "14"}
            {:id 2 :first-name "John2" :last-name "Dow2" :age "54"}
            {:id 3 :first-name "John3" :last-name "Dow3" :age "34"}
            {:id 4 :first-name "John4" :last-name "Dow4" :age "12"}
            {:id 5 :first-name "John5" :last-name "Dow5" :age "24"}]))

I have learned how to filter it by one key, for example:

(defn my-filter
  [str-input]
  (filter #(re-find (->> (str str-input)
                         (lower-case)
                         (re-pattern))
                         (lower-case (:first-name %)))
            @data))

> (my-filter "John1")
> ({:last-name "Dow1", :age "14", :first-name "John1", :id 1})

But now I'm a little bit confused on how to filter data by :first-name, :last-name and :age simple way?

Update: Sorry for being not too clear enough in explanation of what the problem is... Actually, I want all keys :first-name, :last-name and :age to paticipate in filter function, so that, if str-input doesn't match :first-name's val, check if it matches :last-name's val and so on.

Update 2: After trying some-fn, every-pred and transducers, I didn't get what I need, e.g. regex in filter predicates, I guess it's a lack of knowledge for now. So, I ended up with this function which works fine, but the code is ugly and duplicated. How I can get rid of code duplication?

(defn my-filter [str-input]
  (let [firstname (filter #(re-find (->> (str str-input)
                                         (upper-case)
                                         (re-pattern))
                                    (upper-case (:first-name %)))
                     @data)
        lastname (filter #(re-find (->> (str str-input)
                                        (upper-case)
                                        (re-pattern))
                                   (upper-case (:last-name %)))
                    @data)
        age (filter #(re-find (->> (str str-input)
                                   (upper-case)
                                   (re-pattern))
                              (upper-case (:age %)))
               @data)]
    (if-not (empty? firstname)
      firstname
      (if-not (empty? lastname)
        lastname
        (if-not (empty? age)
          age)))))

解决方案

This can also be achieved with the help of functional composition, e.g. you can use every-pred function, which creates a function, checking if all the preds are truthy for its arguments, and use it to filter data. For example if you want to find all items with odd :id value having :last-name of "Dow1", "Dow2", or "Dow3" and :age starting with \3:

user> (def data
  [{:id 1 :first-name "John1" :last-name "Dow1" :age "14"}
   {:id 2 :first-name "John2" :last-name "Dow2" :age "54"}
   {:id 3 :first-name "John3" :last-name "Dow3" :age "34"}
   {:id 4 :first-name "John4" :last-name "Dow4" :age "12"}
   {:id 5 :first-name "John5" :last-name "Dow5" :age "24"}])

user> (filter (every-pred (comp odd? :id)
                          (comp #{"Dow1" "Dow2" "Dow3"} :last-name)
                          (comp #{\3} first :age))
              data)

;;=> ({:id 3, :first-name "John3", :last-name "Dow3", :age "34"})

another way to do it, is to use transducers:

user> (sequence (comp (filter (comp odd? :id))
                      (filter (comp #{"Dow1" "Dow2" "Dow3"} :last-name)))
                data)

notice that the actual filtering would happen just once for every item, so it won't create any intermediate collections.

Update

According to your update you need to keep the value when any of the predicates is true, so you can use some function instead of every-pred:

user> (filter #(some (fn [pred] (pred %))
                     [(comp odd? :id)
                      (comp #{"Dow1" "Dow2" "Dow4"} :last-name)
                      (comp (partial = \3) first :age)])
              data)
;;=> ({:id 1, :first-name "John1", :last-name "Dow1", :age "14"} {:id 2, :first-name "John2", :last-name "Dow2", :age "54"} {:id 3, :first-name "John3", :last-name "Dow3", :age "34"} {:id 4, :first-name "John4", :last-name "Dow4", :age "12"} {:id 5, :first-name "John5", :last-name "Dow5", :age "24"})

这篇关于如何在Clojure中通过多个键过滤地图矢量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆