如何在Clojure中通过多个键过滤地图矢量 [英] How to filter vector of maps by multiple keys in 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屋!