Skip to content
This repository was archived by the owner on Oct 4, 2020. It is now read-only.

Add several methods for mapping over Map #145

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions src/Data/Map.purs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,18 @@ module Data.Map
, filterWithKey
, filterKeys
, filter
, mapMaybe
, mapMaybeWithKey
, mapKeys
, mapKeysMaybe
, mapKeysWith
) where

import Prelude

import Data.Eq (class Eq1)
import Data.Foldable (foldl, foldMap, foldr, class Foldable)
import Data.FoldableWithIndex (class FoldableWithIndex)
import Data.FoldableWithIndex (class FoldableWithIndex, foldlWithIndex)
import Data.FunctorWithIndex (class FunctorWithIndex, mapWithIndex)
import Data.List (List(..), (:), length, nub)
import Data.List.Lazy as LL
Expand Down Expand Up @@ -104,9 +109,9 @@ instance foldableMap :: Foldable (Map k) where
foldMap f m = foldMap f (values m)

instance foldableWithIndexMap :: FoldableWithIndex k (Map k) where
foldlWithIndex f z m = foldl (uncurry <<< (flip f)) z $ asList $ toUnfoldable m
foldrWithIndex f z m = foldr (uncurry f) z $ asList $ toUnfoldable m
foldMapWithIndex f m = foldMap (uncurry f) $ asList $ toUnfoldable m
foldlWithIndex f z m = foldl (uncurry <<< (flip f)) z $ asList $ toAscUnfoldable m
foldrWithIndex f z m = foldr (uncurry f) z $ asList $ toAscUnfoldable m
foldMapWithIndex f m = foldMap (uncurry f) $ asList $ toAscUnfoldable m

asList :: forall k v. List (Tuple k v) -> List (Tuple k v)
asList = id
Expand Down Expand Up @@ -640,3 +645,29 @@ filterKeys predicate = filterWithKey $ const <<< predicate
-- | on the value fails to hold.
filter :: forall k v. Ord k => (v -> Boolean) -> Map k v -> Map k v
filter predicate = filterWithKey $ const predicate

mapMaybe :: forall k v u. Ord k => (v -> Maybe u) -> Map k v -> Map k u
mapMaybe f = mapMaybeWithKey (const f)

mapMaybeWithKey :: forall k v u. Ord k => (k -> v -> Maybe u) -> Map k v -> Map k u
mapMaybeWithKey f m = foldlWithIndex f' mempty m
where
f' k acc v = maybe acc (flip (insert k) acc) $ f k v

mapKeys :: forall j k v. Ord j => Ord k => (k -> j) -> Map k v -> Map j v
mapKeys f = mapKeysMaybeWithValueWith (Just <<< f) (\k v _ -> Just (Tuple (f k) v))

mapKeysMaybe :: forall j k v. Ord j => Ord k => (k -> Maybe j) -> Map k v -> Map j v
mapKeysMaybe f = mapKeysMaybeWithValueWith f (\k v _ -> (_ `Tuple` v) <$> f k)

-- | The value at the greater of the two original keys is used as the first argument to c.
mapKeysWith :: forall j k v. Ord j => Ord k => (v -> v -> v) -> (k -> j) -> Map k v -> Map j v
mapKeysWith f g = mapKeysMaybeWithValueWith (Just <<< g) h
where
h k v (Just v') = Just (Tuple (g k) (f v v'))
h k v Nothing = Just (Tuple (g k) v)

mapKeysMaybeWithValueWith :: forall j k v. Ord j => Ord k => (k -> Maybe j) -> (k -> v -> Maybe v -> Maybe (Tuple j v)) -> Map k v -> Map j v
mapKeysMaybeWithValueWith f g m = foldlWithIndex h mempty m
where
h k acc v = maybe acc (flip (uncurry insert) acc) $ g k v <<< (flip lookup acc) =<< f k
35 changes: 35 additions & 0 deletions test/Test/Data/Map.purs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Control.Monad.Eff.Random (RANDOM)
import Data.Array as A
import Data.Foldable (foldl, for_, all)
import Data.Function (on)
import Data.Int (even)
import Data.List (List(Cons), groupBy, length, nubBy, singleton, sort, sortBy)
import Data.List.NonEmpty as NEL
import Data.Map as M
Expand Down Expand Up @@ -333,3 +334,37 @@ mapTests = do
<> ", mmin: " <> show mmin
<> ", mmax: " <> show mmax
<> ", key: " <> show key

log "`mapMaybe Just` is `id`"
quickCheck \(TestMap m :: TestMap String Int) ->
M.mapMaybe Just m === m

log "deleting with `mapMaybe` works"
quickCheck \(TestMap m :: TestMap String Int) ->
M.mapMaybe (const Nothing) m === (M.empty :: M.Map String Int)

log "`mapMaybe` generalizes `filter`"
quickCheck \(TestMap m :: TestMap Int Int) ->
let justIfTrue b a = if b then Just a else Nothing in
M.mapMaybe (\n -> justIfTrue (even n) n) m == M.filter even m

log "`mapKeysMaybe Just` is `id`"
quickCheck \(TestMap m :: TestMap String Int) ->
M.mapKeysMaybe Just m === m

log "deleting with `mapKeysMaybe` works"
quickCheck \(TestMap m :: TestMap String Int) ->
M.mapKeysMaybe (const Nothing) m === (M.empty :: M.Map String Int)

log "`mapKeysMaybe` generalizes `filterKeys`"
quickCheck \(TestMap m :: TestMap Int Int) ->
let justIfTrue b a = if b then Just a else Nothing in
M.mapKeysMaybe (\n -> justIfTrue (even n) n) m == M.filterKeys even m

log "`mapKeysWith` is well-ordered"
quickCheck \(TestMap m :: TestMap Int Int) ->
unit `M.lookup` M.mapKeysWith (\l r -> l) (const unit) m === (\r -> r.value) <$> M.findMax m

log "`mapKeysWith` generalizes `mapKeys`"
quickCheck \(TestMap m :: TestMap Int Int) ->
M.mapKeysWith const (const unit) m === M.mapKeys (const unit) m