我编写了一个函数来“透视”一些数据表,但想知道是否有更简单的方法来实现相同的结果。
(defn pivot-ab-ba
[data]
(r/reduce
(fn [result [ks c]]
(assoc-in result ks c))
{}
;; pivot {a [b1 c1 b2 c2]} => [[b1 a] c1] [[b2 a] c2]
(mapcat (fn [[a bcseq]]
;; pivot [a [b c]] => [[[b a] c]]
(mapcat (fn [[b c]] [[[b a] c]]) bcseq))
data)))
(let [data {1 {:good [1 2] :bad [3 4]}
2 {:good [5 6] :bad [7 8]}}]
(pivot-ab-ba data))
; => {:good {1 [1 2], 2 [5 6]}, :bad {1 [3 4], 2 [7 8]}}
这有效,但似乎过于复杂。
更新:
@TaylorWood在下面提出了一个解决方案。这是修改后的答案,以避免传入被旋转的键:
(defn pivot [data]
(reduce-kv
(fn [acc k v]
(reduce (fn [acc' k'] (assoc-in acc' [k' k] (k' v)))
acc
(keys v)))
{}
data))
更新2:谢谢大家的回答。由于答案的多样性如此之多,因此我对结果进行了简要介绍,以了解其效果。诚然,这是一个测试,但仍然很有趣:
Benchmarks performed with (criterium.core/bench pivot-function)
# Original pivot-ab-ba
Evaluation count : 8466240 in 60 samples of 141104 calls.
Execution time mean : 7.274613 µs
Execution time std-deviation : 108.681498 ns
# @TaylorWood - pivot
Evaluation count : 39848280 in 60 samples of 664138 calls.
Execution time mean : 1.568971 µs
Execution time std-deviation : 32.567822 ns
# @AlanThompson - reorder-tree
Evaluation count : 25999260 in 60 samples of 433321 calls.
Execution time mean : 2.385929 µs
Execution time std-deviation : 33.130731 ns
# @AlanThompson reorder-tree-reduce
Evaluation count : 14507820 in 60 samples of 241797 calls.
Execution time mean : 4.249135 µs
Execution time std-deviation : 89.933197 ns
# @amalloy - pivot
Evaluation count : 12721980 in 60 samples of 212033 calls.
Execution time mean : 5.087314 µs
Execution time std-deviation : 226.242206 ns
答案 0 :(得分:1)
这是另一种方法:
from __future__ import unicode_literals
这将获取一个映射和期望的键,然后对(defn pivot [data ks]
(reduce-kv
(fn [acc k v]
(reduce (fn [acc' k'] (assoc-in acc' [k' k] (k' v)))
acc
ks))
{}
data))
中的每个键/值对进行归约,然后对每个期望的键进行另一个内部归约,从data
映射和assoc中获取其值。将其导入输出映射。
data
答案 1 :(得分:0)
这就是我要怎么做。我正在使用atom
来累积结果,但是如果您确实需要,可以将其转换为reduce
:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test) )
(defn reorder-tree
[data]
(let [result (atom {})]
(doseq [[letter gb-data] data]
(doseq [[gb-key nums-data] gb-data]
(swap! result assoc-in [gb-key letter] nums-data)))
@result))
(dotest
(let [data {:a {:good [1 2]
:bad [3 4]}
:b {:good [5 6]
:bad [7 8]}}
expected {:good {:a [1 2]
:b [5 6]}
:bad {:a [3 4]
:b [7 8]}}]
(is= expected (reorder-tree data))))
更新:好的,我无法抗拒使用嵌套的reduce
来编写for
版本:
(defn reorder-tree-reduce
[data]
(reduce
(fn [cum-map [letter gb-key nums-data]]
(assoc-in cum-map [gb-key letter] nums-data))
{}
(for [[letter gb-data] data
[gb-key nums-data] gb-data]
[letter gb-key nums-data])))
答案 2 :(得分:0)
Reduce是实现此目标的不必要的低级功能。我更愿意以一种不言而喻的正确方式来生成一系列地图,然后使用merge
来组合它们。将组合逻辑与生产逻辑交织在一起,会使函数的读者更难查看其作用以及是否正确。取而代之的是依靠简单易懂的功能merge
和merge-with
,这意味着没有多余的复杂性需要重新理解。
(defn pivot [coll]
(apply merge-with merge
(for [[a m] coll
[b x] m]
{b {a x}})))
答案 3 :(得分:0)
快速perc
枢纽:
(let [data {1 {:good [1 2] :bad [3 4]}
2 {:good [5 6] :bad [7 8]}}]
(-> data
(#%/%{:bad (into {} (map #%/$[(key $) (-> $ val :bad)] %))
:good (into {} (map #%/$[(key $) (-> $ val :good)] %))})))