2016-08-28 2 views
1

Я разбираю некоторые данные XML из Stack Exchange с помощью clojure.data.xml, например, если я разбираю данные Votes, он возвращает LazySeq, содержащий HashMap для каждой строки данных.Выполнять «получить» на всех элементах HashMap в LazySeq

То, что я пытаюсь сделать, это получить значения, связанные только с определенными ключами, для каждой строки, например, (get votes [:Id :CreationDate]). Я пробовал много вещей, большинство из которых приводят к ошибкам.

Ближайший я мог бы добраться до того, что мне нужно, используя (doall (map get votes [:Id :CreationDate])). Тем не менее, проблема, которую я испытываю сейчас, что я не могу вернуть больше, чем просто в первой строке (т.е. (1 2011-01-19T00:00:00.000))

Вот MCVE, которая может быть запущена на любом Clojure REPL или on Codepad online IDE.

В идеале я хотел бы вернуть какую-то коллекцию или карту, которая содержит значения, которые мне нужны для каждой строки, конечная цель - написать что-то вроде файла CSV или такого. Например, карта, как

(1 2011-01-19T00:00:00.000 
2 2011-01-19T00:00:00.000 
3 2011-01-19T00:00:00.000 
4 2011-01-19T00:00:00.000) 
(def votes '({:Id "1", 
       :PostId "2", 
       :VoteTypeId "2", 
       :CreationDate "2011-01-19T00:00:00.000"} 
      {:Id "2", 
       :PostId "3", 
       :VoteTypeId "2", 
       :CreationDate "2011-01-19T00:00:00.000"} 
      {:Id "3", 
       :PostId "1", 
       :VoteTypeId "2", 
       :CreationDate "2011-01-19T00:00:00.000"} 
      {:Id "4", 
       :PostId "1", 
       :VoteTypeId "2", 
       :CreationDate "2011-01-19T00:00:00.000"})) 

    (println (doall (map get votes [:Id :CreationDate]))) 

Дополнительная деталь: Если это какой-либо помощи/интерес, код, я использую, чтобы получить над ленивым SEQ выглядит следующим образом:

(ns se-datadump.read-xml 
    (require 
    [clojure.data.xml :as xml]) 

(def xml-votes 
    "<votes><row Id=\"1\" PostId=\"2\" VoteTypeId=\"2\" CreationDate=\"2011-01-19T00:00:00.000\" /> <row Id=\"2\" PostId=\"3\" VoteTypeId=\"2\" CreationDate=\"2011-01-19T00:00:00.000\" /> <row Id=\"3\" PostId=\"1\" VoteTypeId=\"2\" CreationDate=\"2011-01-19T00:00:00.000\" /> <row Id=\"4\" PostId=\"1\" VoteTypeId=\"2\" CreationDate=\"2011-01-19T00:00:00.000\" /></votes>") 

(defn se-xml->rows-seq 
    "Returns LazySequence from a properly formatted XML string, 
    which contains a HashMap for every <row> element with each of its attributes. 
    This assumes the standard Stack Exchange XML format, where a parent element contains 
    only a series of <row> child elements with no further hierarchy." 
    [xml-str] 
    (let [xml-records (xml/parse-str xml-str)] 
     (map :attrs (-> xml-records :content)))) 

; this returns a map identical as in the MCVE: 
(def votes (se-xml->rows-seq xml-votes) 
+0

Я не уверен, что полностью понимаю ваше намерение. Не могли бы вы предоставить результат, созданный вручную? чем это легче сказать. –

+0

@AntonHarald Я добавил пример желаемого результата, надеюсь, что это поможет сделать его более понятным. – Phrancis

ответ

3

Вы, видимо, нужно juxt:

(map (juxt :Id :CreationDate) votes) 
;; => (["1" "2011-01-19T00:00:00.000"] ["2" "2011-01-19T00:00:00.000"] ["3" "2011-01-19T00:00:00.000"] ["4" "2011-01-19T00:00:00.000"]) 

Если вам нужна карта из него:

(into {} (map (juxt :Id :CreationDate) votes)) 
;; => {"1" "2011-01-19T00:00:00.000", "2" "2011-01-19T00:00:00.000", "3" "2011-01-19T00:00:00.000", "4" "2011-01-19T00:00:00.000"} 
+0

О, вау, это работает как шарм! – Phrancis

2

Прежде всего, позвольте мне объяснить, что часть кода вы предлагаете в CodePad на самом деле делает. Я сомневаюсь, что это вещь, которую вы намерены делать:

(println (doall (map get votes [:Id :CreationDate]))) 

Решающая часть: (map get votes [:Id :CreationDate]) Это отображает более две коллекции: ленивых последовательность «голоса» и вектор. Всякий раз, когда отображение более чем одной коллекции, возвращаемая ленивая последовательность будет до тех пор, пока будет предоставлена ​​самая короткая коллекция.
Например можно отобразить через конечный набор и бесконечную последовательность:

(map + (range) [1 2 3]) 
;; (0 3 5) 

Это объясняет, почему ваш результат только две вещи длиной:

(map get votes [:Id :CreationDate]) 

сводится к:

((get (votes 0) ([:Id :CreationDate] 0) 
(get (votes 1) ([:Id :CreationDate] 1)) 

сокращает до:

((get {:Id "1", 
     :PostId "2", 
     :VoteTypeId "2", 
     :CreationDate "2011-01-19T00:00:00.000"} :Id) 
(get {:Id "2", 
     :PostId "3", 
     :VoteTypeId "2", 
     :CreationDate "2011-01-19T00:00:00.000"} :CreationDate)) 

уменьшает наконец:

(1 2011-01-19T00:00:00.000) 

Это просто для понимания цели. Если компилятор делает именно эти шаги, это еще один вопрос.

doall здесь не нужно, так как println уже делает это неявно.


Как уже отмечалось. В вашем случае вам лучше использовать juxt и только карту за голоса. Если вы действительно хотите иметь выход образца, вам также необходимо сгладить выход:

(flatten (map (juxt :Id :CreationDate) votes)) 
+0

Это имеет смысл, не думал об этом именно так. Спасибо за объяснение! – Phrancis

Смежные вопросы