Clojure:有效地“转置”一个中等大小的地图列表

时间:2011-11-21 19:12:30

标签: performance list clojure maps storage

我需要一种非常快速有效的方法来“转置”clojure中的地图列表。

让我说我有:

(def monthly-sales [{:month 1 :pc "A" :sales 100} 
 {:month 2 :pc "B" :sales 200} ... {:month 12 :pc "Z" :sales 100}])

我需要有类似的东西:

 |PC|1|2|3|4|5|6|7|8|9|10|11|12|
 |A|100||||||||||||
 |Etc.|

我回答以下问题:

 (let [grouped (group-by (apply juxt [:month]) monthly-sales)]
       (apply str (interpose "\n" 
     (for [k (distinct (map :pc rows))] 
           (str "|" k "|" (clojure.string/join "|" 
         (for [n (range 1 13)]
               (get (first (filter #(= (:pc %) k) (get grouped [n]))) :sale))))))))))))

基本上我按月对所有值进行分组(分组,注意它可以通过“apply juxt”键入多于1个键),这是该列的关键。完成后,我推断pc的唯一值,这将是行的关键。休息应该是自我解释的。

您认为这是明确的clojurian设计吗?它可以更有效和清晰吗?

有用的链接: http://pramode.net/clojure/2010/06/01/lazy-sequences-in-clojure/

2 个答案:

答案 0 :(得分:3)

习惯性的clojure库(如clojure.java.jdbc)将这些长列表作为惰性seq提供。这意味着你只需要足够的内存来包含一行以及加载clojure和库的通常开销 - 只要你从文件或数据库中获取数据并将其写入流/ db /中,而不是将其全部保存在存储器中。

至于你要求的变换,给定一个名为result-set的行(map),如:

(interpose "\n"
  (map (fn [row]
    (clojure.string/join "|" (map row [:consumer :product ...]))
    result-set)))

将为您提供一个懒惰的seq,您可以将其转储到文件中以生成类似于|的东西你想要的分开的数据。

附录:至于“快速” - 除非你的存储设置不常见,否则这可能比你的存储I / O快得多 - 而且它很直接。

答案 1 :(得分:0)

本文中没有任何内容表明您希望通过处理此数据集实现最终目标。至少,我认为主要想法可能是将1GB的数据放入HTML表格中。因此,没有信息可以给出如何最好地实现这一点。只需重新排列相同的数据就不会产生任何有意义的结果,也不会改变您之后要执行的操作的内存或访问要求。

首先,您显示为“基础”数据的内容看起来可能是至少三个关系表上的联接查询的结果(如果已正确规范化)。通过SQL直接从这些表中获取信息可能更有效,在Clojure本身处理之前已经减少了信息量,过滤或排序。

如果不是,那么正确地将数据标准化并将其存储在数据库中可能是一种选择,但所有这些都取决于您最终想要对数据做什么。