我有这样的数据结构:
[{ :2007-08-05 [ { :meat-weight-gain 100} {:meat-weight-loss 80} {:meat-balance 20}]},
{ :2007-08-06 [ { :meat-weight-gain 10} {:meat-weight-loss 60} {:meat-balance -30}]},
{ :2007-08-07 [ { :meat-weight-gain 40} {:meat-weight-loss 80} {:meat-balance -70}]}
{ :2007-08-08 [ { :meat-weight-gain 100} {:meat-weight-loss 0} {:meat-balance 30}]}]
如何迭代它并返回肉平衡为负时的数据周期?样本数据将是这样的:
[ {:end-period-balance -70, :period-start 2007-08-06, :period-end 2007-08-07 } ]
除此之外,我可以改进我的数据结构还是已经可以了?如果有,怎么样?非常感谢你。
答案 0 :(得分:4)
我建议您将数据形状更改为元组列表,每个元组包含日期和平衡数据图。就像这样:
(def data [[:2007-08-05 { :meat-weight-gain 100 :meat-weight-loss 80 :meat-balance 20}],
[:2007-08-06 { :meat-weight-gain 10 :meat-weight-loss 60 :meat-balance -30}],
[:2007-08-07 { :meat-weight-gain 40 :meat-weight-loss 80 :meat-balance -70}]
[:2007-08-08 { :meat-weight-gain 100 :meat-weight-loss 0 :meat-balance 30}]
[:2007-08-09 { :meat-weight-gain 19 :meat-weight-loss -20 :meat-balance -10}]])
然后很容易按重量增加/减少(使用partition-by
)对时段进行分类并收集所需信息:
user> (let [parts (partition-by #(-> % second :meat-balance neg?) data)]
(keep #(let [[p-start _] (first %)
[p-end {balance :meat-balance}] (last %)]
(when (neg? balance)
{:period-start p-start
:period-end p-end
:end-period-balance balance}))
parts))
;;=> ({:period-start :2007-08-06, :period-end :2007-08-07, :end-period-balance -70}
;; {:period-start :2007-08-09, :period-end :2007-08-09, :end-period-balance -10})
或包含日期的地图列表:
(def data [{:date :2007-08-05 :meat-weight-gain 100 :meat-weight-loss 80 :meat-balance 20},
{:date :2007-08-06 :meat-weight-gain 10 :meat-weight-loss 60 :meat-balance -30},
{:date :2007-08-07 :meat-weight-gain 40 :meat-weight-loss 80 :meat-balance -70}
{:date :2007-08-08 :meat-weight-gain 100 :meat-weight-loss 0 :meat-balance 30}
{:date :2007-08-09 :meat-weight-gain 100 :meat-weight-loss 0 :meat-balance -10}])
user> (let [parts (partition-by #(-> % :meat-balance neg?) data)]
(keep #(let [{p-start :date} (first %)
{p-end :date balance :meat-balance} (last %)]
(when (neg? balance)
{:period-start p-start
:period-end p-end
:end-period-balance balance}))
parts))
;;=> ({:period-start :2007-08-06, :period-end :2007-08-07, :end-period-balance -70}
;; {:period-start :2007-08-09, :period-end :2007-08-09, :end-period-balance -10})
<强>更新强>
如果您确实需要初始数据格式,那么您可以使用相同的方法,只需重新定义值检索部分:
user> (defn meat-balance [rec]
(some :meat-balance (-> rec first second)))
user> (let [parts (partition-by #(-> % meat-balance neg?) data)]
(keep #(let [p-start (-> % first ffirst)
p-end (-> % last ffirst)
balance (-> % first meat-balance)]
(when (neg? balance)
{:period-start p-start
:period-end p-end
:end-period-balance balance}))
parts))
;;=> ({:period-start :2007-08-06, :period-end :2007-08-07, :end-period-balance -30})
答案 1 :(得分:0)
首先,复杂的输入数据结构可以解开:
data: JSON.stringify(data),
dataType:'json'
...进入(map (juxt ffirst (comp first #(keep :meat-balance %) val first)))
;;=> ([:2007-08-05 20] [:2007-08-06 -30] [:2007-08-07 -70] [:2007-08-08 30])
的元组。
请注意,到目前为止,我们保留了正面和负面的肉类余额。答案需要消极的运行,即连续的负肉平衡。 [date-keyword meat-balance]
是任何运行的转到函数,之后我们可以过滤以仅获取答案所需的分区组。之前我们需要partition-by
,因为您的日期键最初位于地图中,地图未分类。在排序,分区和过滤之后,我们准备提供答案,这只需要将我们的规范sort
数据结构转换为所需的结构:
[date-keyword meat-balance]
答案 2 :(得分:0)
更改数据格式:
:meat-weight-balance
数据 - 这是多余的。(前两个更改跟随@leetwinski's advice)
我们得到......
(def data
{:2007-08-05 {:meat-weight-gain 100, :meat-weight-loss 80},
:2007-08-06 {:meat-weight-gain 10, :meat-weight-loss 60},
:2007-08-07 {:meat-weight-gain 40, :meat-weight-loss 80},
:2007-08-08 {:meat-weight-gain 100, :meat-weight-loss 0}})
条目恰好按日期顺序排列,因为它是一张小地图。如果我们想确保日期顺序,我们最好有一个有序的地图:
(def sorted-data (into (sorted-map) data))
这看起来并没有什么不同,但总是以按键顺序显示数据,这是 - 幸运的是 - 日期顺序。
这似乎是一个很长的路要走,以便将记录转换为向量中的原始顺序,但是向量中有未使用的日期关键字顺序: D on&#39; t R epeat Y 我们自己。
让我们计算每日余额:
(def balances
(map-vals #(- (:meat-weight-gain %) (:meat-weight-loss %)) sorted-data))
balances
=> {:2007-08-05 20, :2007-08-06 -50, :2007-08-07 -40, :2007-08-08 100}
...其中map-vals
函数是map
和mapv
的类似函数,适用于地图的值:
(defn map-vals [f m]
(into (empty m) (map (fn [[k v]] [k (f v)])) m))
请注意,它会返回与给定的相同种类的地图,在这种情况下是已排序的地图。
我们想知道什么时期有净减肥。目前还不清楚这意味着什么。让我们看看从一开始的净重增加:
(reductions (fn [[_ av] [k v]] [k (+ av v)]) balances)
=> ([:2007-08-05 20] [:2007-08-06 -30] [:2007-08-07 -70] [:2007-08-08 30])
或者我们可以将序列划分为获取和丢失部分:
(partition-by (fn [[_ v]] (neg? v)) balances)
=> (([:2007-08-05 20]) ([:2007-08-06 -50] [:2007-08-07 -40]) ([:2007-08-08 100]))
我们需要partition-by
的变体,用group-by
作为区分函数的值来键入其子序列。然后你知道什么是增益范围,什么是失败范围。一个便宜又开朗的版本......
(defn group-partition-by [f coll]
(let [parts (partition-by f coll)]
(map #(-> % first f (list %)) parts)))
然后
(group-partition-by (fn [[_ v]] (neg? v)) balances)
=> ((false ([:2007-08-05 20]))
(true ([:2007-08-06 -50] [:2007-08-07 -40]))
(false ([:2007-08-08 100])))
您可能希望将此数据从日期范围减少到总计余额中的(已排序)地图。
转换
我们如何从given
转到data
?我们可以直接转到sorted-data
,如下所示:
(def sorted-data
(->> given
(into (sorted-map))
(map-vals (comp #(into {} %) #(remove :meat-balance %)))))
sorted-data
=>
{:2007-08-05 {:meat-weight-gain 100, :meat-weight-loss 80},
:2007-08-06 {:meat-weight-gain 10, :meat-weight-loss 60},
:2007-08-07 {:meat-weight-gain 40, :meat-weight-loss 80},
:2007-08-08 {:meat-weight-gain 100, :meat-weight-loss 0}}
曝光
注意的
你最好使用欧洲日期,而不是美国日期,否则你需要一个聪明的keyfn
来获取日期序列中的记录。我更喜欢将clj-time
local-date
s作为关键字
答案 3 :(得分:0)
正如上面已经说过的那样,您的数据不能很好地用于此目的。这是一个循序渐进的解决方案:
准备您的数据:
(def data
[{ :2007-08-05 [ { :meat-weight-gain 100} {:meat-weight-loss 80} {:meat-balance 20}]},
{ :2007-08-06 [ { :meat-weight-gain 10} {:meat-weight-loss 60} {:meat-balance -30}]},
{ :2007-08-07 [ { :meat-weight-gain 40} {:meat-weight-loss 80} {:meat-balance -70}]}
{ :2007-08-08 [ { :meat-weight-gain 100} {:meat-weight-loss 0} {:meat-balance 30}]}])
创建新的数据结构:
(defn turner [stats]
(apply merge
{:year (-> stats keys first)}
(-> stats vals first)))
(def data2 (mapv turner data))
[{:year :2007-08-05, :meat-weight-gain 100, :meat-weight-loss 80, :meat-balance 20}
{:year :2007-08-06, :meat-weight-gain 10, :meat-weight-loss 60, :meat-balance -30}
{:year :2007-08-07, :meat-weight-gain 40, :meat-weight-loss 80, :meat-balance -70}
{:year :2007-08-08, :meat-weight-gain 100, :meat-weight-loss 0, :meat-balance 30}]
现在,您通过谓词对数据进行分组,该谓词检查余额是否为负数:
(partition-by #(-> % :meat-balance neg?) (sort-by :year data2))
(({:year :2007-08-05, :meat-weight-gain 100, :meat-weight-loss 80, :meat-balance 20})
({:year :2007-08-06, :meat-weight-gain 10, :meat-weight-loss 60, :meat-balance -30}
{:year :2007-08-07, :meat-weight-gain 40, :meat-weight-loss 80, :meat-balance -70})
({:year :2007-08-08, :meat-weight-gain 100, :meat-weight-loss 0, :meat-balance 30}))
让它成为data3
。然后,过滤该数据结构只得到负数:
(filter #(-> % first :meat-balance neg?) data3)
(({:year :2007-08-06, :meat-weight-gain 10, :meat-weight-loss 60, :meat-balance -30}
{:year :2007-08-07, :meat-weight-gain 40, :meat-weight-loss 80, :meat-balance -70}))
让它成为data4
。现在你得到了界限:
{:period-start (-> data4 first first :year)
:period-end (-> data4 first last :year)
:end-period-balance (-> data4 first last :meat-balance)}
是什么让你完全
{:period-start :2007-08-06,
:period-end :2007-08-07,
:end-period-balance -70}