【问题标题】:Merge list of maps and combine values to sets in Clojure合并地图列表并将值组合到 Clojure 中的集合
【发布时间】:2010-02-04 21:05:36
【问题描述】:

我可以将什么函数作为 FOO 放在这里以在最后产生 true ?我玩过 hash-set(仅对前 2 个值正确)、conj 和 concat,但我知道我没有正确处理单元素 vs set 条件。

(defn mergeMatches [propertyMapList]
    "Take a list of maps and merges them combining values into a set"
    (reduce #(merge-with FOO %1 %2) {} propertyMapList))

(def in 
    (list
        {:a 1}
        {:a 2}
        {:a 3}
        {:b 4}
        {:b 5}
        {:b 6} ))

(def out
    { :a #{ 1 2 3}
      :b #{ 4 5 6} })

; this should return true
(= (mergeMatches in) out)

处理这个问题最惯用的方法是什么?

【问题讨论】:

    标签: functional-programming clojure


    【解决方案1】:

    这样就可以了:

    (let [set #(if (set? %) % #{%})]
      #(clojure.set/union (set %) (set %2)))
    

    为示例更直接地重写(Alex):

    (defn to-set [s]
        (if (set? s) s #{s}))
    (defn set-union [s1 s2] 
        (clojure.set/union (to-set s1) (to-set s2)))
    (defn mergeMatches [propertyMapList]
        (reduce #(merge-with set-union %1 %2) {} propertyMapList))
    

    【讨论】:

    • a) 由于没有嵌套匿名函数的限制,它也是无效的。 b) 不太漂亮! :)
    • 另外,我认为这将返回一组地图而不是具有设定值的地图。
    • @Alex: a) 没有这样的限制。您不能嵌套匿名函数literals(使用#(...) 创建),但可以嵌套匿名函数(尽管您需要使用fn)。 b) 我猜是口味问题。 :-) 此外,您应该尝试实际运行它,因为它生成的函数实际上确实使您的测试表达式返回 true,当您在 reduce 表达式中替换 FOO 时。
    • 作为事后的想法,请注意,如果 in 集合包含具有设定值的地图,那么这些值将在最终产品中变平。如果这是不可取的,那么实际上最好降到loop / recur,因为任何基于reduce 的解决方案都可能使用非常复杂的归约函数并且需要对结果图进行后处理。 (最重要的原因是merge-with 仅在左映射不包含给定键时才调用合并函数。)
    • @Alex:我当然不会试图将我的答案按原样硬塞到你的代码中——我会把它分解成一个单独定义的 fn(如果你真的想要的话,命名为 FOO)您可以在合并表单中参考。这对你来说是一个练习。 :-) 但是不,它不会产生一组映射——用作合并中的 fn,它会产生一个具有设定值的映射。合并永远不会产生集合。
    【解决方案2】:

    这不是我写的,但这是@amitrathoreTwitter上的contributed

    (defn kv [bag [k v]] 
      (update-in bag [k] conj v))
    (defn mergeMatches [propertyMapList]
      (reduce #(reduce kv %1 %2) {} propertyMapList))
    

    【讨论】:

    • 总而言之,我认为这是最简单且最容易理解的解决方案。还依赖于@Timothy Pratley 的回答等更新。我认为这个的主要问题是它产生了一个列表映射而不是一个集合映射,这并没有给我我需要的重复数据删除。
    【解决方案3】:

    我不会为此使用合并,

    (defn fnil [f not-found]
      (fn [x y] (f (if (nil? x) not-found x) y)))
    (defn conj-in [m map-entry]
      (update-in m [(key map-entry)] (fnil conj #{}) (val map-entry)))
    (defn merge-matches [property-map-list]
      (reduce conj-in {} (apply concat property-map-list)))
    
    user=> (merge-matches in)
    {:b #{4 5 6}, :a #{1 2 3}}
    

    fnil 将很快成为核心的一部分,因此您可以忽略实现......但它只是创建了另一个可以处理 nil 参数的函数的版本。在这种情况下,conj 将用 #{} 替换 nil。

    因此,对于所提供的映射列表中的每个键/值,减少结合到一个集合。

    【讨论】:

    • 我喜欢这个作为解决方案,它使用的东西正在移动到核心库中。 @amitrathore 的解决方案是相同的基本思想,但更简单。我还没有确定它实际上在功能上是否有任何不同。
    • 所以不同之处在于,另一种解决方案会生成列表映射,而不是集合映射,这对我来说不太好。
    • 在进一步使用时,此解决方案的问题在于它无法处理传入地图已经将集合作为值的情况。 (是的,我添加要求较晚。:) 例如:user=> (println (mergeMatches (list {:a #{1 2 3}} {:a #{4 5 6}}))) {:a #{#{1 2 3} #{4 5 6}}} 当我真的想要 {:a #{1 2 3 4 5 6}} 结果时。 (println (mergeMatches in2))
    【解决方案4】:

    @wmacgyverTwitter 上基于multimaps 贡献的另一个解决方案:

    (defn add
      "Adds key-value pairs the multimap."
      ([mm k v]
         (assoc mm k (conj (get mm k #{}) v)))
      ([mm k v & kvs]
         (apply add (add mm k v) kvs)))
    (defn mm-merge
      "Merges the multimaps, taking the union of values."
      [& mms]
      (apply (partial merge-with union) mms))   
    
    (defn mergeMatches [property-map-list]
      (reduce mm-merge (map #(add {} (key (first %)) (val (first %))) property-map-list)))      
    

    【讨论】:

    • 我喜欢明确构建(或使用)多图库来处理这种数据结构并简单地依赖它的想法。根据我最终使用它的程度,我实际上可能会长期朝着这个方向发展。
    【解决方案5】:

    这似乎有效:

    (defn FOO [v1 v2]
          (if (set? v1)
              (apply hash-set v2 v1)
              (hash-set v1 v2)))
    

    【讨论】:

      【解决方案6】:

      不是很漂亮,但很管用。

      (defn mergeMatches [propertyMapList]
          (for [k (set (for [pp propertyMapList] (key (first pp))))]
               {k (set (remove nil? (for [pp propertyMapList] (k pp))))}))
      

      【讨论】:

      • 非常聪明。我对使用这么多嵌套列表推导来维护代码有点警惕! :) 我认为其他一些解决方案更具可读性和可理解性。
      • 是的,它变得有点密集,但我一直不明白为什么在 Clojure 中没有更频繁地使用它。有关更简洁的示例,请参阅 bestinclass.dk/index.php/2010/02/clojure-list-comprehension
      猜你喜欢
      • 1970-01-01
      • 2018-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-27
      相关资源
      最近更新 更多