简体   繁体   English

如何通过Clojure中的多个键过滤地图矢量

[英]How to filter vector of maps by multiple keys in Clojure

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?但是现在我对如何通过:first-name:last-name:age简单方法过滤数据感到有些困惑?

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.更新:抱歉,在解释问题时不够清楚……实际上,我希望所有键:first-name:last-name:age都能参与过滤功能,这样,如果str-input不匹配:first-name的 val,检查它是否匹配:last-name的 val 等等。

Update 2 : After trying some-fn , every-pred and transducers , I didn't get what I need, eg regex in filter predicates, I guess it's a lack of knowledge for now.更新 2 :在尝试some-fnevery-predtransducers ,我没有得到我需要的东西,例如过滤谓词中的正则表达式,我想这是现​​在缺乏知识。 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)))))

I think this would just work for you.我认为这对你有用。 Using the fact that in Clojure :first-name is a function that can be used to look up its corresponding value in a hashmap.使用 Clojure 中的 :first-name 是一个函数,可用于在哈希图中查找其对应的值。

(defn find-all 
  [field value data] 
  (filter #(= value (field %)) data))

This will return a list of the matching hashmaps in your vector.这将返回向量中匹配哈希图的列表。

user=> (find-all :first-name "John1" @data)
({:id 1, :first-name "John1", :last-name "Dow1", :age "14"})

I would suggest you to store age as integer instead of string if you do not have a strong case not to.如果您没有强烈的理由不这样做,我建议您将年龄存储为整数而不是字符串。

More about keywords:有关关键字的更多信息:

(:key map) (:键映射)

  • works when your map is nil当您的地图为零时有效
user=> (:key-word nil) nil
  • can be used with map or filter可以与地图或过滤器一起使用
user=> (map :last-name @data) ("Dow1" "Dow2" "Dow3" "Dow4" "Dow5")
  • :key cannot be nil :key 不能为零
user=> (nil (first @data)) CompilerException java.lang.IllegalArgumentException: Can't call nil, form: (nil (first (clojure.core/deref data))),

compiling:(/private/var/folders/nr/g50ld9t91c555dzv91n43bg40000gn/T/form-init5403593628725666667.clj:1:1)编译:(/private/var/folders/nr/g50ld9t91c555dzv91n43bg40000gn/T/form-init5403593628725666667.clj:1:1)

(map :key) (地图:键)

  • better when :key is nil当 :key 为零时更好
user=> ((first @data) nil) nil

This can also be achieved with the help of functional composition, eg 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.这也可以在函数组合的帮助下实现,例如您可以使用every-pred函数,它创建一个函数,检查所有 pred 的参数是否为真,并使用它来过滤数据。 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 :例如,如果您想查找所有奇数:id值的项目,其:last-name"Dow1", "Dow2","Dow3"并且:age\\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 :根据您的更新,当任何谓词为真时,您需要保留该值,因此您可以使用some函数而不是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"})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM