Skip to content

Commit b37b524

Browse files
committed
Add fromListUpsert for HashMap
Add fromListUpsert for compatibility with containers, which added it in PR #1190: fromListUpsert :: Hashable k => (a -> Maybe v -> v) -> [(k, a)] -> HashMap k v This builds a map from a list, using the provided function to combine values. Equivalent to performing an upsert for each key/value pair. Assisted-by: GLM-5 via Fireworks AI <noreply@fireworks.ai>
1 parent 96e01c0 commit b37b524

5 files changed

Lines changed: 65 additions & 0 deletions

File tree

Data/HashMap/Internal.hs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ module Data.HashMap.Internal
112112
, fromList
113113
, fromListWith
114114
, fromListWithKey
115+
, fromListUpsert
115116

116117
-- ** Internals used by the strict version
117118
, Hash
@@ -2746,6 +2747,33 @@ fromListWithKey :: Hashable k => (k -> v -> v -> v) -> [(k, v)] -> HashMap k v
27462747
fromListWithKey f = List.foldl' (\ m (k, v) -> unsafeInsertWithKey (\k' a b -> (# f k' a b #)) k v m) empty
27472748
{-# INLINE fromListWithKey #-}
27482749

2750+
-- | \(O(n \log n)\) Construct a map from a list of elements. Uses
2751+
-- the provided function to combine values.
2752+
--
2753+
-- The result is equivalent to performing an 'upsert' for every key\/value
2754+
-- in the list.
2755+
--
2756+
-- @
2757+
-- fromListUpsert f = 'List.foldl'' (\\m (k, x) -> 'upsert' (f x) k m) 'empty'
2758+
-- @
2759+
--
2760+
-- ==== __Examples__
2761+
--
2762+
-- Group all values by their keys:
2763+
--
2764+
-- > let xs = [('a', 1), ('b', 2), ('a', 3)]
2765+
-- > in fromListUpsert (\x -> maybe [x] (x:)) xs
2766+
-- >
2767+
-- > = fromList [('a', [3, 1]), ('b', [2])]
2768+
--
2769+
-- Note that the lists in the resulting map contain elements in reverse order
2770+
-- from their occurrences in the original list.
2771+
--
2772+
-- @since 0.2.21
2773+
fromListUpsert :: Hashable k => (a -> Maybe v -> v) -> [(k, a)] -> HashMap k v
2774+
fromListUpsert f = List.foldl' (\ m (k, x) -> upsert (f x) k m) empty
2775+
{-# INLINE fromListUpsert #-}
2776+
27492777
------------------------------------------------------------------------
27502778
-- Array operations
27512779

Data/HashMap/Internal/Strict.hs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ module Data.HashMap.Internal.Strict
125125
, fromList
126126
, fromListWith
127127
, fromListWithKey
128+
, fromListUpsert
128129
) where
129130

130131
import Control.Applicative (Const (..))
@@ -757,6 +758,33 @@ fromListWithKey :: Hashable k => (k -> v -> v -> v) -> [(k, v)] -> HashMap k v
757758
fromListWithKey f = List.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) HM.empty
758759
{-# INLINE fromListWithKey #-}
759760

761+
-- | \(O(n \log n)\) Construct a map from a list of elements. Uses
762+
-- the provided function to combine values.
763+
--
764+
-- The result is equivalent to performing an 'upsert' for every key\/value
765+
-- in the list.
766+
--
767+
-- @
768+
-- fromListUpsert f = 'List.foldl'' (\\m (k, x) -> 'upsert' (f x) k m) 'empty'
769+
-- @
770+
--
771+
-- ==== __Examples__
772+
--
773+
-- Group all values by their keys:
774+
--
775+
-- > let xs = [('a', 1), ('b', 2), ('a', 3)]
776+
-- > in fromListUpsert (\x -> maybe [x] (x:)) xs
777+
-- >
778+
-- > = fromList [('a', [3, 1]), ('b', [2])]
779+
--
780+
-- Note that the lists in the resulting map contain elements in reverse order
781+
-- from their occurrences in the original list.
782+
--
783+
-- @since 0.2.21
784+
fromListUpsert :: Hashable k => (a -> Maybe v -> v) -> [(k, a)] -> HashMap k v
785+
fromListUpsert f = List.foldl' (\ m (k, x) -> upsert (f x) k m) HM.empty
786+
{-# INLINE fromListUpsert #-}
787+
760788
------------------------------------------------------------------------
761789
-- Array operations
762790

Data/HashMap/Lazy.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ module Data.HashMap.Lazy
105105
, fromList
106106
, fromListWith
107107
, fromListWithKey
108+
, fromListUpsert
108109

109110
-- ** HashSets
110111
, HS.keysSet

Data/HashMap/Strict.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ module Data.HashMap.Strict
104104
, fromList
105105
, fromListWith
106106
, fromListWithKey
107+
, fromListUpsert
107108

108109
-- ** HashSets
109110
, HS.keysSet

tests/Properties/HashMapLazy.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,13 @@ tests =
486486
, testProperty "valid" $
487487
\(Fn3 f) (kvs :: [(Key, A)]) -> isValid (HM.fromListWithKey f kvs)
488488
]
489+
, testGroup "fromListUpsert"
490+
[ testProperty "model" $
491+
\(Fn2 f :: Fun (A, Maybe A) A) (kvs :: [(Key, A)]) ->
492+
toOrdMap (HM.fromListUpsert f kvs) === M.fromListUpsert f kvs
493+
, testProperty "valid" $
494+
\(Fn2 f :: Fun (A, Maybe A) A) (kvs :: [(Key, A)]) -> isValid (HM.fromListUpsert f kvs)
495+
]
489496
, testProperty "toList" $
490497
\(m :: HMKI) -> List.sort (HM.toList m) === List.sort (M.toList (toOrdMap m))
491498
]

0 commit comments

Comments
 (0)