clojure中的日期

时间:2017-05-28 22:45:53

标签: vector clojure

我有这样的数据结构:

[{ :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 } ]

除此之外,我可以改进我的数据结构还是已经可以了?如果有,怎么样?非常感谢你。

4 个答案:

答案 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函数是mapmapv的类似函数,适用于地图的值:

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

曝光

  • 你必须彻底了解sequence library
  • 地图的相应设施不在表面上。去 与transducers的握手会有所帮助 - 不确定多少。

注意

你最好使用欧洲日期,而不是美国日期,否则你需要一个聪明的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}