我有一个如下所示的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]
答案 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"])]]