[英]Clojure: I have many sorted maps and want to reduce in order all there values a super maps of keys -> vector
[英]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 :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"}]))
我已经学会了如何一键过滤,例如:
(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})
但是现在我对如何通过:first-name
、 :last-name
和:age
简单方法过滤数据感到有些困惑?
更新:抱歉,在解释问题时不够清楚……实际上,我希望所有键:first-name
、 :last-name
和:age
都能参与过滤功能,这样,如果str-input
不匹配:first-name
的 val,检查它是否匹配:last-name
的 val 等等。
更新 2 :在尝试some-fn
、 every-pred
和transducers
,我没有得到我需要的东西,例如过滤谓词中的正则表达式,我想这是现在缺乏知识。 所以,我最终得到了这个工作正常的函数,但代码很丑陋且重复。 我怎样才能摆脱代码重复?
(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)))))
我认为这对你有用。 使用 Clojure 中的 :first-name 是一个函数,可用于在哈希图中查找其对应的值。
(defn find-all
[field value data]
(filter #(= value (field %)) data))
这将返回向量中匹配哈希图的列表。
user=> (find-all :first-name "John1" @data)
({:id 1, :first-name "John1", :last-name "Dow1", :age "14"})
如果您没有强烈的理由不这样做,我建议您将年龄存储为整数而不是字符串。
有关关键字的更多信息:
(:键映射)
user=> (:key-word nil) nil
user=> (map :last-name @data) ("Dow1" "Dow2" "Dow3" "Dow4" "Dow5")
user=> (nil (first @data)) CompilerException java.lang.IllegalArgumentException: Can't call nil, form: (nil (first (clojure.core/deref data))),
编译:(/private/var/folders/nr/g50ld9t91c555dzv91n43bg40000gn/T/form-init5403593628725666667.clj:1:1)
(地图:键)
user=> ((first @data) nil) nil
这也可以在函数组合的帮助下实现,例如您可以使用every-pred
函数,它创建一个函数,检查所有 pred 的参数是否为真,并使用它来过滤数据。 例如,如果您想查找所有奇数: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"})
另一种方法是使用传感器:
user> (sequence (comp (filter (comp odd? :id))
(filter (comp #{"Dow1" "Dow2" "Dow3"} :last-name)))
data)
请注意,实际过滤对于每个项目只会发生一次,因此它不会创建任何中间集合。
更新
根据您的更新,当任何谓词为真时,您需要保留该值,因此您可以使用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.