2017-03-07 32 views
0
过滤由多个键映射的矢量

假设我们有这样一个数据结构:如何用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过滤器功能paticipate,因此,如果str-input不匹配:first-name的val,检查它是否与:last-name的val等相匹配。

更新2: 试图some-fnevery-predtransducers后,我没有得到我所需要的,例如过滤器谓词中的正则表达式,我想现在是缺乏知识。 所以,我结束了这个功能正常,但代码是丑陋的和重复的。我如何摆脱代码重复?

(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))))) 

回答

1

这也可以用来实现功能组成的帮助,例如你可以使用every-pred函数,该函数创建一个函数,检查所有pred是否真实,并用它来过滤数据。例如,如果你想找到具有"Dow1", "Dow2",:last-name"Dow3":age:id值开始与\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"}) 

另一种方式来做到这一点,是使用transducers

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

注意实际的过滤只会对每个项目发生一次,所以它不会创建任何中间集合。

更新

根据您的更新,你需要保持的值时的predicetes的任何是真实的,所以你可以使用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"}) 
+1

“every-pred”的“或”对应词是'some-fn'。你可以在第一个例子中切换它们,不需要使用'some'。 – madstap

+0

没错。忘了它 – leetwinski

+0

@leetwinski,你能否检查第二次更新?谢谢 – graaf

1

我认为这只适用于你。使用Clojure中的事实:first-name是一个函数,可以用来查找散列表中的相应值。

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

这将返回向量中匹配hashmaps的列表。

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))), 

编译:(/私营/无功/文件夹/ NR/g50ld9t91c555dzv91n43bg40000gn/T /形状init5403593628725666667。CLJ:1:1)

(图:键)

  • 时更好:关键是nil
user=> ((first @data) nil) 
nil 
+0

谢谢你答案!抱歉,对于问题的解释不够清楚...请问您是否可以检查最新的问题?谢谢。 – graaf