【问题标题】:Perform "get" on all HashMap elements of a LazySeq对 LazySeq 的所有 HashMap 元素执行“获取”
【发布时间】:2016-08-28 22:00:22
【问题描述】:

我正在使用 clojure.data.xml 解析来自 Stack Exchange 的一些 XML 数据,例如,如果我解析 Votes 数据,它会返回一个包含每行数据的 HashMap 的 LazySeq。

我要做的是为每一行获取仅与某些键关联的值,例如(get votes [:Id :CreationDate])。我尝试了很多事情,其中​​大多数导致铸造错误。

最接近我需要的是使用(doall (map get votes [:Id :CreationDate]))。但是,我现在遇到的问题是我似乎只能返回第一行(即(1 2011-01-19T00:00:00.000)

这是一个可以在任何 Clojure REPL 或 on Codepad online IDE 上运行的 MCVE。

理想情况下,我想返回某种集合或映射,其中包含每行所需的值,最终目标是写入 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])))

附加细节:如果这有任何帮助/兴趣,我用来获取上述惰性序列的代码如下:

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

【问题讨论】:

  • 我不确定我是否完全理解您的意图。您能否提供手动创建的示例结果?比说起来更容易。
  • @AntonHarald 我添加了一个示例期望结果,希望这有助于使其更清晰。

标签: clojure hashmap lazy-sequences


【解决方案1】:

你显然需要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"}

【讨论】:

    【解决方案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))
    

    【讨论】:

    • 说的有道理,没想到是这样的。谢谢你的解释!
    猜你喜欢
    • 2015-07-08
    • 2013-05-27
    • 1970-01-01
    • 2013-06-04
    • 2014-09-07
    • 2016-11-15
    • 2022-11-01
    • 2021-07-22
    • 1970-01-01
    相关资源
    最近更新 更多