4

korma 関数からの結果セットに対して ETL を実行するために、いくつかのフォームをつなぎ合わせる際に問題が発生しています。

korma 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である「子」を取得して、この結果セットをフィルタリングしようとしています。

私の試み:

;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 引数として渡さない理由がわかりません。

ありとあらゆる助けが大歓迎です。

ありがとう

4

3 に答える 3

3

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

それはあなたが望んだものです。問題は、フォームに thread-last を使用する必要があったことですfilter。この問題を回避する最も信頼できる方法は、常に明示的であり、Clojureas->スレッド形式を使用するか、Tupelo ライブラリit->を使用することです。

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

前の 2 つのソリューションのいずれかを見ると、各ステージの出力がパイプラインの次の関数への最後の引数として使用されていることがわかります。したがって、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->フォームを使用して、パイプラインの各ステージで中間値をどのように使用するかを明示する方が常に明確であることがわかりました。

于 2015-05-28T04:06:29.277 に答える
1

最初のスレッドがフィルター関数を適用せず、:children ベクトルを coll 引数として渡さない理由がわかりません。

これはまさにthread-firstマクロが行うことです。

clojuredocs.orgから:

expr をフォームに通します。最初のフォームの 2 番目の項目として x を挿入し、まだリストでない場合はリストを作成します。

したがって、あなたの場合、アプリケーションは次のようになりますfilter

(filter [...] #(= (% :a) 1))

(の代わりに)を使用する必要がある場合は、部分的に適用することでこれを回避できます。thread-firstthread-lastfilter

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

; [{:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4}]
于 2015-05-28T03:44:11.243 に答える