具有过滤功能的 Clojure 线程优先
Clojure thread-first with filter function
我在将一些表单串在一起以对 korma 函数的结果集执行一些 ETL 时遇到问题。
我从科尔马回来sql:
({: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"})
我希望通过获取 :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)))
我在这里期待的是哈希映射向量,其中 :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))
有效。我不确定为什么第一个线程没有应用过滤器函数,将 :children
向量作为 coll 参数传递。
非常感谢任何帮助。
谢谢
您想要 thread-last
宏:
(->> data first :children (filter #(= (% :a) 1)))
产量
({:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4})
您原始代码中的 thread-first
宏相当于这样写:
(filter (:children (first data)) #(= (% :a) 1))
这会导致错误,因为您的匿名函数不是序列。
I'm not sure why the first-thread is not applying the filter function, passing in the :children vector as the coll argument.
这正是 thread-first
宏所做的。
Threads the expr through the forms. Inserts x as the
second item in the first form, making a list of it if it is not a
list already.
因此,在您的情况下,filter
的应用最终为:
(filter [...] #(= (% :a) 1))
如果您必须使用thread-first
(而不是thread-last
),那么您可以通过部分应用filter
及其谓词:
(->
data
first
:children
((partial filter #(= (:a %) 1)))
vec)
; [{:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4}]
thread-first (->
) 和 thread-last (->>
) 宏总是有问题的,因为在选择一个而不是另一个时很容易出错(或混合使用他们就像你在这里所做的那样)。像这样分解步骤:
(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})
这就是你想要的。问题是您确实需要为 filter
形式使用 thread-last。避免此问题的最可靠方法是始终明确并使用 Clojure as->
线程形式,或者更好的是 it->
from the Tupelo library:
(def result (it-> data
(first it)
(:children it)
(filter #(= (% :a) 1) 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})
我们还可以查看前面两个解决方案中的任何一个,并注意每个阶段的输出用作管道中下一个函数的 last 参数。因此,我们也可以用 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->
形式来明确管道的每个阶段应该如何使用中间值总是更清楚。
我在将一些表单串在一起以对 korma 函数的结果集执行一些 ETL 时遇到问题。
我从科尔马回来sql:
({: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"})
我希望通过获取 :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)))
我在这里期待的是哈希映射向量,其中 :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))
有效。我不确定为什么第一个线程没有应用过滤器函数,将 :children
向量作为 coll 参数传递。
非常感谢任何帮助。
谢谢
您想要 thread-last
宏:
(->> data first :children (filter #(= (% :a) 1)))
产量
({:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4})
您原始代码中的 thread-first
宏相当于这样写:
(filter (:children (first data)) #(= (% :a) 1))
这会导致错误,因为您的匿名函数不是序列。
I'm not sure why the first-thread is not applying the filter function, passing in the :children vector as the coll argument.
这正是 thread-first
宏所做的。
Threads the expr through the forms. Inserts x as the second item in the first form, making a list of it if it is not a list already.
因此,在您的情况下,filter
的应用最终为:
(filter [...] #(= (% :a) 1))
如果您必须使用thread-first
(而不是thread-last
),那么您可以通过部分应用filter
及其谓词:
(->
data
first
:children
((partial filter #(= (:a %) 1)))
vec)
; [{:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4}]
thread-first (->
) 和 thread-last (->>
) 宏总是有问题的,因为在选择一个而不是另一个时很容易出错(或混合使用他们就像你在这里所做的那样)。像这样分解步骤:
(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})
这就是你想要的。问题是您确实需要为 filter
形式使用 thread-last。避免此问题的最可靠方法是始终明确并使用 Clojure as->
线程形式,或者更好的是 it->
from the Tupelo library:
(def result (it-> data
(first it)
(:children it)
(filter #(= (% :a) 1) 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})
我们还可以查看前面两个解决方案中的任何一个,并注意每个阶段的输出用作管道中下一个函数的 last 参数。因此,我们也可以用 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->
形式来明确管道的每个阶段应该如何使用中间值总是更清楚。