如何优雅地反转Map的键和值?

时间:2014-02-03 22:10:13

标签: haskell

我有一个如下所示的Map(或关联列表):

[("A", ["KB", "KC"]), ("B", ["KD", "KE"])]

如何简洁地转换上面的Map,以便键是值,值是键,所以结果应该是这样的?

[("KB", "A"), ("KC", "A"), ("KD", "B"), ("KE", "B")]

修改

这是我的解决方案

invertAList xs = [(val,key) |  (key, vals) <- xs, val <- vals]

4 个答案:

答案 0 :(得分:12)

这里的一个关键问题是如何处理“右手边”作业中值出现多次的情况:

import Data.Map (Map)
import qualified Data.Map as Map

-- "KB" occurs twice.
example = Map.fromList [("A", ["KB", "KC"]), ("B", ["KD", "KB"])]

解决方案是使用Map.fromListWith

invert :: (Ord k, Ord v) => Map k [v] -> Map v [k]
invert m = Map.fromListWith (++) pairs
    where pairs = [(v, [k]) | (k, vs) <- Map.toList m, v <- vs]

{-
>>> invert (Map.fromList [("A", ["KB", "KC"]), ("B", ["KD", "KE"])])
fromList [("KB",["A"]),("KC",["A"]),("KD",["B"]),("KE",["B"])]

>>> invert (Map.fromList [("A", ["KB", "KC"]), ("B", ["KD", "KB"])])
fromList [("KB",["B","A"]),("KC",["A"]),("KD",["B"])]
-}

答案 1 :(得分:6)

怎么样

Prelude> let xs = [("A", ["KB", "KC"]), ("B", ["KD", "KE"])]
Prelude> concatMap (\(k, vs) -> [(v, k) | v <- vs]) xs
[("KB","A"),("KC","A"),("KD","B"),("KE","B")]

答案 2 :(得分:1)

import qualified Data.Map   as M
import           Data.Tuple

swapMap :: M.Map String [String] -> M.Map String String
swapMap = M.fromList . concatMap swapper . M.toList
  where swapper (x, [y,z]) = [(y,x),(z,x)]

*Main> swapMap $ M.fromList [("A", ["KB", "KC"]), ("B", ["KD", "KE"])]
fromList [("KB","A"),("KC","A"),("KD","B"),("KE","B")]

答案 3 :(得分:1)

反转Map也可以通过折叠来完成。

import Data.Set as Set
import Data.Map as Map

invert :: (Ord k, Ord v) => Map k v -> Map v (Set k)
invert = foldlWithKey (\acc k v -> insertWith (Set.union) v (Set.singleton k) acc) Map.empty

此外,如果原始Map是双头投射,那么所有新值都应该是单例,或者我们可以编写单独的函数并将Set排除在类型之外。

invertBijection :: (Ord k, Ord v) => Map k v -> Map v k
invertBijection = foldrWithKey (flip Map.insert) Map.empty

顺便说一下,这与使用折叠

反转列表的方法相同
reverse :: [a] -> [a]
reverse = foldl (flip (:)) []

为了使反转按照您指定的方式工作(添加条件使其变平),您可以编写一个函数。这是一个这样的功能的一个例子(这可以稍微更通用,但它现在适合我们的目的):

import Data.Foldable as Foldable

flattenKeys :: (Ord k, Ord v, Foldable t) => Map (t k) v -> Map k (Set v)
flattenKeys = foldlWithKey (\acc keys v -> Map.unionsWith Set.union (acc : fmap (`Map.singleton` Set.singleton v) (Foldable.toList keys))) Map.empty

这样,如果您的密钥是一个列表,它会将列表的每个元素添加为密钥。

*Main> flattenKeys (Map.fromList [([1,2], "a"), ([2,3], "b")])
fromList [(1,fromList ["a"]),(2,fromList ["a","b"]),(3,fromList ["b"])]]