2015-05-28 3 views
2

У меня возникла проблема, связанная с некоторыми формами, чтобы сделать некоторый ETL в результирующем наборе из функции korma.clojure first-thread with filter function

я вернусь из Корме 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))) 

Что я ожидал здесь вектор HashMaps, что: установлен в 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.

Любая помощь очень ценится.

Благодаря

ответ

5

Вы хотите thread-last макрос:

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

дает

({: а 1,: B 2,: с 3} {: 1,: B 3, : с 4})

thread-first макрос в исходном коде эквивалентно записи:

(filter (:children (first data)) #(= (% :a) 1)) 

Это приводит к ошибке, поскольку ваша анонимная функция не является последовательностью.

0

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.

От clojuredocs.org:

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}] 
2

Нить-первых (->) и токарно-последний (- >>) макросы всегда проблематичны в том, что это легко сделать ошибку в выборе одного над другим (или при смешивании их в качестве вы сделали здесь). Разбейте шаги, как так:

(ns tstclj.core 
    (:use cooljure.core) ; see https://github.com/cloojure/cooljure 
    (: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 cooljure.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 ...). Самый надежный способ избежать этой проблемы, чтобы всегда быть явными и использовать как-> форму резьба:

(def result (as-> data it 
        (first it) 
        (:children it) 
        (filter #(= (% :a) 1) it))) 

Bye с помощью резьбы первого, вы случайно написали эквивалент этого:

(def result (as-> data it 
        (first it) 
        (:children it) 
        (filter it #(= (% :a) 1)))) 

и ошибка отражает тот факт, что fn #(= (% :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}) 

Мы могли бы также посмотреть на любом из двух предыдущих решений, и обратите внимание, что на выходе каждой ступени используются в качестве последнего аргумент следующей функции в конвейере. Таким образом, мы могли бы также решить с поточно-последняя:

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

Если ваша цепь обработки не очень просто, я считаю, что это просто о всегда понятнее использовать как-> форму, чтобы быть четко о том, как промежуточное значение должны использоваться каждым этапом трубопровода.