首页 > 解决方案 > 压缩两个 HashMap

问题描述

假设我有两个 HashMap:

import Data.HashMap.Strict
m1 :: HashMap Char Int
m2 :: HashMap Char Int
??? :: (v1 -> v2 -> v3) -> HashMap k v1 -> HashMap k v2 -> HashMap k v3
-- or even
??? :: (k -> v1 -> v2 -> v3) -> HashMap k v1 -> HashMap k v2 -> HashMap k v3

我想要某种方式zip走过这些地图,其中:

  1. 如果一个键存在于一个映射中但另一个映射中不存在,则将其替换为默认值(类似于“zip 最长”
  2. 结果类型可能与原始类型不同。

intersectionWithKey这与更接近 的语义的签名相匹配unionWithKey

一个具体的例子:对于一个(,)使用 0 默认值的简单案例,我可以这样做

unionWith add (map (,0) m1) (map (0,) m2) :: HashMap Char (Int, Int)
add (a, x) (b, y) = (a+b, x+y)

这引发了一个问题:一般来说有没有一种干净的方法来做到这一点?

标签: haskell

解决方案


一般来说:

  1. 合并两个映射以获取所有键的列表
  2. 定义一个新函数,给定一个键,用于lookupDefault从每个映射中获取适当的值(默认值或真实值),以便计算新映射的值
  3. 将该函数映射到键上,并根据结果创建一个新的哈希映射。

zipMap :: (v1 -> v2 -> v3) -- Combining function
       -> v1               -- default value for keys unique to the second map
       -> v2               -- default value for keys unique to the first map
       -> HashMap k v1
       -> HashMap k v2
       -> HashMap k v3
zipMap f d1 d2 m1 m2 = let allKeys = keys (union m1 m2)
                           f' k = (k, f (lookupDefault d1 k m1) (lookupDefault d2 k m2))
                       in fromList $ map f' allKeys

推荐阅读