Filter Clojure线程优先使用过滤器函数

Filter Clojure线程优先使用过滤器函数,filter,clojure,sqlkorma,Filter,Clojure,Sqlkorma,我在将一些表单串在一起以对korma函数的结果集进行ETL时遇到问题 我从korma sql返回: ({:ID1:some_字段“asd”:children[{:A1:B2:C3}{:A1:B3:C4}{:A2:B2:C3}]:另一个_字段“qwe”}) 我希望通过获取:a关键字为1的“children”来过滤这个结果集 我的尝试: ;mock of korma result (def data '({:id 1 :some_field "asd" :children [{:a 1 :b 2 :

我在将一些表单串在一起以对korma函数的结果集进行ETL时遇到问题

我从korma sql返回:

({:ID1:some_字段“asd”:children[{:A1:B2:C3}{:A1:B3:C4}{:A2:B2:C3}]:另一个_字段“qwe”})

我希望通过获取
:a
关键字为1的“children”来过滤这个结果集

我的尝试:

;mock of korma result
(def data '({:id 1 :some_field "asd" :children [{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4} {:a 2 :b 2 :c 3}] :another_field "qwe"}))

(-> data 
    first 
    :children 
    (filter #(= (% :a) 1)))
这里我期望的是一个hashmaps向量:a设置为1,即:

[{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4}]
但是,我得到了以下错误:

IllegalArgumentException Don't know how to create ISeq from: xxx.core$eval3145$fn__3146  clojure.lang.RT.seqFrom (RT.java:505)
从我收集的错误来看,它试图从一个函数中创建一个序列……尽管无法连接点来解释原因

此外,如果我通过执行以下操作完全分离筛选函数:

(let [children (-> data first :children)] 
    (filter #(= (% :a) 1) children))
它起作用了。我不确定为什么第一个线程没有应用filter函数,将
:children
向量作为coll参数传入

非常感谢所有帮助


谢谢

您希望
线程最后一个
宏:

(->> data first :children (filter #(= (% :a) 1)))
屈服

({:A1,:B2,:C3}{:A1,:B3,:C4})

原始代码中的
线程优先
宏相当于编写:

(filter (:children (first data)) #(= (% :a) 1))
这会导致错误,因为匿名函数不是序列

我不知道为什么第一个线程没有应用filter函数,将:children向量作为coll参数传入

这正是
线程优先
宏所做的

从:

在表单中穿行expr。插入x作为 第一种形式中的第二项,如果不是 已经列出了

因此,在您的情况下,
filter
的应用程序最终是:

(过滤器[…]#(=(:a)1))

如果必须先使用
线程
(而不是
后线程
),则可以通过部分应用
过滤器及其谓词来解决此问题:

(->
  data
  first
  :children
  ((partial filter #(= (:a %) 1)))
  vec)

; [{:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4}]
线程优先(
->
)和线程最后(
->
)宏总是有问题的,因为在选择其中一个宏时很容易出错(或者像您在这里所做的那样将它们混合在一起)。将步骤分解如下:

(ns tstclj.core
  (:use cooljure.core)  ; see https://github.com/cloojure/tupelo/
  (:gen-class))

(def data [ {:id 1 :some_field "asd" 
             :children [ {:a 1 :b 2 :c 3} 
                          {:a 1 :b 3 :c 4}
                          {:a 2 :b 2 :c 3} ] 
             :another_field "qwe"} ] )

(def v1    (first data))
(def v2    (:children v1))
(def v3    (filter #(= (% :a) 1) v2))

(spyx v1)    ; from tupelo.core/spyx
(spyx v2)
(spyx v3)
您将获得如下结果:

v1 => {:children [{:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1} {:c 3, :b 2, :a 2}], :another_field "qwe", :id 1, :some_field "asd"}
v2 => [{:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1} {:c 3, :b 2, :a 2}]
v3 => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})
这正是你想要的。问题是,对于
过滤器
表单,您确实需要使用thread last。避免此问题的最可靠方法是始终明确使用Clojure
as->
线程形式,或者更好地使用
it->
来自:

通过先使用线程,您意外地编写了以下等效代码:

(def result (it-> data 
                  (first it)
                  (:children  it)
                  (filter it #(= (% :a) 1))))
该错误反映了函数
#(=((:a)1)
无法转换为seq的事实。有时,使用
let
表单并为中间结果命名是值得的:

(let [result-map        (first data)
      children-vec      (:children  result-map)
      a1-maps           (filter #(= (% :a) 1) children-vec) ]
  (spyx a1-maps))
;;-> a1-maps => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})
我们还可以查看前面两个解决方案中的任何一个,并注意到每个阶段的输出都被用作管道中下一个函数的最后一个参数。因此,我们也可以使用thread last解决它:

(def result3  (->>  data
                    first
                    :children
                    (filter #(= (% :a) 1))))
(spyx result3)
;;-> result3 => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})
除非您的处理链非常简单,否则我发现使用
it->
表单来明确说明管道的每个阶段应该如何使用中间值总是比较清楚的

(def result3  (->>  data
                    first
                    :children
                    (filter #(= (% :a) 1))))
(spyx result3)
;;-> result3 => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})