mapMaybe用于Data.Map中的键

问题描述

我想将以下功能映射到Map的键上

f :: a -> Maybe b

并丢弃nothing键,并保留Just键,但从Just提取。就像Map.mapMaybe,但要输入密钥

mapMaybeKeys :: (a -> Maybe b) -> Map a c -> Map b c

我在Hoogle中搜索了这种类型的签名,但没有找到任何内容

我可以这样做:

mapMaybeKeys f
    = Map.toList
    . catMaybes
    . fmap (fmap swap . traverse f . swap)
    . Map.toList

或:

mapMaybeKeys f
   = Map.mapKeys fromJust
   . Map.delete nothing
   . Map.mapKeys f

还有更优雅的方式吗?

解决方法

具有列表理解力

import Data.Map (Map)
import qualified Data.Map as M
import Control.Arrow (first)

mapMaybeKeys :: Ord b => (a -> Maybe b) -> Map a c -> Map b c
mapMaybeKeys f m = 
   M.fromList [ (b,a) | (Just b,a) <- map (first f) . M.toList $ m ]
,

使用foldMapWithKey(或foldrWithKey)也很不错,而无需浏览中间列表。

mapMaybeKeys :: (Ord b) => (a -> Maybe b) -> Map a c -> Map b c

mapMaybeKeys f = Map.foldMapWithKey
  (\ k a -> foldMap (\ k' -> M.singleton k' a) (f k))

-- or:
mapMaybeKeys f = Map.foldMapWithKey (flip (foldMap . flip M.singleton) . f)

-- or:
mapMaybeKeys = M.foldMapWithKey . fmap (flip (foldMap . flip M.singleton))

如果键函数返回foldMap,则内部Nothing会生成一个空映射。您可以添加一些帮助程序定义,以通过避免使用flip来使它看起来更简单。请注意,您需要Ord约束才能使用b作为结果映射的键。

,

谢谢@Will Ness和@Jon Purdy,我喜欢您的解决方案,但经过一些调整,我认为我最喜欢我的原始解决方案:

import Data.Bitraversable (bitraverse)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (catMaybes)

mapMaybeKeys :: (Ord b) => (a -> Maybe b) -> Map a c -> Map b c
mapMaybeKeys f
  = Map.fromList
  . catMaybes
  . fmap (bitraverse f Just)
  . Map.toList