2016-04-15 30 views
2

我正试图想出一个功能性解决方案来解决由交替设置和数值组成的自定义数据结构中的重复项。考虑自定义clojure向量中的重复项

一个例子:

(def a [#{:a} 0.1 #{:b} 0.3 #{:a :b} 0.1 #{:a} 0.3 #{:b} 0.1 #{:a} 0.1]) 

我要补充相应的复制集导致

[#{:a} 0.5 #{:b} 0.4 #{:a :b} 0.1] 

我可以做值这个使用loop/recur但不知道是否有一种方法,它使用Clojure中的高阶函数。

感谢。

+1

你可以分享你的循环/复发解决方案吗? – jmargolisvt

+0

对的顺序是否重要?否则,您可以将数据保存为从关键字集合到序列(矢量?)数字的映射。 – Thumbnail

回答

1

我会将源矢量折叠到一个映射图中,并将键作为数值聚合,然后展开为一个矢量。

(->> a 
    (partition 2) 
    (reduce (fn [acc [k v]] 
       (if (get acc k) 
       (update acc k (partial + v)) 
       (assoc acc k v))) 
      {}) 
    (reduce (fn [acc [k v]] 
       (into acc [k v])) 
      [])) 
5
(reduce 
    (fn [acc [k v]] 
    (update acc k (fnil + 0) v)) 
    {} 
    (partition 2 a)) 

首先,划分你的序列与partition键值对,然后reduce那些对。诀窍是使用fnil,它将替代nil,该密钥尚未添加到acc,但值为0

这就给了你一张地图:

{#{:a} 0.5, #{:b} 0.4, #{:b :a} 0.1} 

如果你需要它作为价值的平序列,你可以通过它通过seqflatten:(只是为了好玩

(->> a 
    (partition 2) 
    (reduce 
    (fn [acc [k v]] 
     (update acc k (fnil + 0) v)) 
    {}) 
    (apply concat) 
    (into [])) 

;; => (#{:a} 0.5 #{:b} 0.4 #{:b :a} 0.1) 
+0

我从来没有用过'fnil'。谢谢你指出。十分优雅。 – endbegin

+0

不客气:)。我忽略了你需要一系列值,而不是地图。我已经更新了我的答案。 –

+0

我知道我没有把你的答案标记为“接受”,但它是。希望我可以将两者都视为已被接受。我做了一个'(变成[](seq(flatten)...)'来最后得到一个向量 – endbegin

4

多一个变种,因为reduce版本显然有更好的表现):

(->> a 
    (partition 2) 
    (group-by first) 
    (map (fn [[k v]] [k (apply + (map second v))])) 
    (reduce into []))