以下是您可以在描述问题时专门解决此问题的一种方法 - 如中所示,没有任何普遍性:
(def data
[{:date "2012-6-6" :region "US" :status 1}
{:date "2012-6-10" :region "UK" :status 2}
{:date "2012-6-10" :region "US" :status 1}
{:date "2012-6-11" :region "UK" :status 3}]
=> (map (fn [[date data-points]]
(apply assoc {:date date}
(mapcat (juxt (comp keyword :region)
:status)
data-points)))
(group-by :date
data)))
=> ({:US 1, :date "2012-6-6"}
{:US 1, :UK 2, :date "2012-6-10"}
{:UK 3, :date "2012-6-11"})
它的工作原理是按日期对数据进行分组,然后映射一个函数(我将在几秒钟内讨论该函数)。group-by
通过在元素上调用某个函数并将对该函数具有相同返回值的元素进行分组,从而对序列中的元素进行分组。它制作了一个哈希映射,其中每个唯一的返回值都是一个键,映射中的每个值都是原始序列中返回该值的元素的序列。例如,以下是您的数据按日期分组的样子:
{"2012-6-6" [{:status 1
:date "2012-6-6"
:region "US"}]
"2012-6-10" [{:status 2
:date "2012-6-10"
:region "UK"}
{:status 1
:date "2012-6-10"
:region "US"}]
"2012-6-11" [{:status 3
:date "2012-6-11"
:region "UK"}]}
这就是 group-by 的工作原理。现在映射到这个分组数据的函数将绑定date
到每个序列的第一个元素,以及每个序列data-points
的第二个元素。我们将对(apply assoc {:date date} ...)
数据点进行“重组”,其中 at 的值:region
现在是 at 的值的关键字:status
。“重组”是通过在每个数据点上调用mapcat
一个data-points
函数来实现的。juxt
(comp keyword :region)
:status
为了希望阐明这个juxt
-ed 函数在做什么,我给出了如果它被写出来的等价物会是什么样子:
(defn above-juxted-fn
[data-point]
[(keyword (:region data-point))
(:status data-point)])
现在,实际上这不是一个完整的解决方案,因为您似乎希望将所有可能的区域作为关键字包含在内,并且如果该区域和日期没有数据,则该值为零。但是,根据上述解决方案的返回结果,无论何时(get region-key-here)
对任何数据点执行操作,都可以改为执行(get region-key-here 0)
,因此如果查找未能找到给定的区域键,则默认值为 0。
另一件事:如果您正在处理大量数据,我不相信这会是一种特别快速的做事方式(也许其他人会在这里有更准确和详细的建议)。我写它的目的是为了简单和极简。
另外,正如我上面所说,它没有普遍性。至于一般性,我们可以通过将其成型为一个接受三个参数的函数来改变它:1)一个映射序列 2)一个分组键和 3)一个“new-key-at”“new-val-at”序列" 对,我称之为重组规范(例如,在上面,这将是[:region :status]
)。开始:
(defn group-and-restructure
[data grouping-key & restructuring-specs]
(let [grouped-data (group-by grouping-key
data)
restructuring-fn (apply juxt
(mapcat (fn [[k1 k2]]
[(comp keyword k1)
k2])
restructuring-specs))]
(map (fn [[grouping-k-value
data-points]]
(apply assoc {grouping-key grouping-k-value}
(mapcat restructuring-fn
data-points)))
grouped-data)))
=> (group-and-restructure
[{:date "2012-6-6" :region "US" :status 1}
{:date "2012-6-10" :region "UK" :status 2}
{:date "2012-6-10" :region "US" :status 1}
{:date "2012-6-11" :region "UK" :status 3}]
:date
[:region :status])
=> ({:US 1, :date "2012-6-6"}
{:US 1, :UK 2, :date "2012-6-10"}
{:UK 3, :date "2012-6-11"})
这与上面的特定解决方案非常相似。主要区别在于重组规范被转换为一个函数,当mapcat
-ted down 时data-points
,将返回一个新的 key/val 对序列,以assoc
-ed 到新的返回数据中。最好的部分是此功能允许您灵活地重组数据。例如,您可以按 分组:region
和重组[:date :status]
,如下所示:
=> (group-and-restructure
[{:date "2012-6-6" :region "US" :status 1}
{:date "2012-6-10" :region "UK" :status 2}
{:date "2012-6-10" :region "US" :status 1}
{:date "2012-6-11" :region "UK" :status 3}]
:region
[:date :status])
=> ({:2012-6-10 1, :2012-6-6 1, :region "US"}
{:2012-6-11 3, :2012-6-10 2, :region "UK"})