2024 年 Clojure 状态调查!中分享您的想法。

欢迎!请查看关于页面以了解有关如何使用本站的一些更多信息。

0
Clojure

有时根据数据的语义进行合并是很重要的。不幸的是,当前的 merge-with 实现让你根据数据类型(值)猜测需要做什么。然而,语义需要我们知道我们试图合并的键是什么

例如,在一个应用中,一个键 :messages 是一个数组,需要合并时进行连接,而 :tags 键需要替换为新值

(merge-with into 
   {:messages [1 2 3] :tags [1 1 1]} 
   {:messages [4] :tags [4 4 4]}) 

=> {:messages [1 2 3 4] :tags [1 1 1 4 4 4]} ;; got
=> {:messages [1 2 3 4] :tags [4 4 4]} ;; expected

由于当前的 merge-with 函数从未向合并函数提供键,所以无法实现这一点

3 答案

+1

被选中
 
最佳答案

merge-with 是一个很出色的工具,但它不能解决所有问题。即使能够“提供键”也无法满足某些情况。幸运的是,merge-with 不是一个原始函数;您可以自由地创建自己的函数来完成您需要的操作!有一些现成的选项,功能非常强大:“Meander”(https://github.com/noprompt/meander)和“Specter”(https://github.com/redplanetlabs/specter

我在找解决方案时偶然发现了这个问题 :)

甚至“提供密钥”在某些情况下也将不足以处理 --- 好奇我漏掉了哪些情况
我同意这个观点 - merge-with 就是这样,如果你需要更多,在现有的实用库中有许多选项,无论是小的(如 http://plumatic.github.io/plumbing/plumbing.map.html#var-merge-with-key)还是大规模的(如 meander 或 specter)。
+2

这是一个晚到的回答,但一个干净的可能不会引入 clojure.core 的解决方案是使用 reduce-kv 而不是 merge-with

(defn newer [x y] (if (pos? (compare x y)) x y))
(defn combine [m k v]
  (case k
    :messages (update m k into v)
    :tags (update m k newer v)))
(reduce-kv combine
  {:messages [1 2 3] :tags [1 1 1]}
  {:messages [4] :tags [4 4 4]})
;; => {:messages [1 2 3 4] :tags [4 4 4]}
0

此外,认为可以有一个适用于所有键的单个合并函数是不正确的。例如,如果有一个键是标量数字,那么作为合并函数的 into 将因为

IllegalArgumentException 无法从:java.lang.Long 创建 ISeq 而失败

...