Skip to content

Commit feeafd3

Browse files
authored
Adds Dict.Extra.{invertAll,upsert,updateIfExists} (#62)
1 parent ec5c3ce commit feeafd3

File tree

3 files changed

+118
-3
lines changed

3 files changed

+118
-3
lines changed

docs.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

src/Dict/Extra.elm

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Dict.Extra exposing
22
( groupBy, filterGroupBy, fromListBy, fromListCombining, fromListByCombining, frequencies
3-
, removeWhen, removeMany, keepOnly, insertCombining, mapKeys, filterMap, invert
3+
, removeWhen, removeMany, keepOnly, insertCombining, updateIfExists, upsert, invert, invertAll
4+
, mapKeys, filterMap
45
, any, all
56
, find
67
, unionWith
@@ -16,7 +17,12 @@ module Dict.Extra exposing
1617
1718
# Manipulation
1819
19-
@docs removeWhen, removeMany, keepOnly, insertCombining, mapKeys, filterMap, invert
20+
@docs removeWhen, removeMany, keepOnly, insertCombining, updateIfExists, upsert, invert, invertAll
21+
22+
23+
# Maps
24+
25+
@docs mapKeys, filterMap
2026
2127
2228
# Predicates
@@ -243,6 +249,46 @@ insertCombining combine key value dict =
243249
Dict.update key with dict
244250

245251

252+
{-| Updates a value if the key is present in the dictionary, leaves the dictionary untouched otherwise.
253+
254+
import Dict
255+
256+
Dict.fromList [ ( "expenses", 38.25 ), ( "assets", 100.85 ) ]
257+
|> updateIfExists "expenses" (\amount -> amount + 2.50)
258+
|> updateIfExists "liabilities" (\amount -> amount - 2.50)
259+
--> Dict.fromList [ ( "expenses", 40.75 ), ( "assets", 100.85 ) ]
260+
261+
-}
262+
updateIfExists : comparable -> (a -> a) -> Dict comparable a -> Dict comparable a
263+
updateIfExists key f dict =
264+
case Dict.get key dict of
265+
Just value ->
266+
Dict.insert key (f value) dict
267+
268+
Nothing ->
269+
dict
270+
271+
272+
{-| Updates a value if the key is present in the dictionary, inserts a new key-value pair otherwise.
273+
274+
import Dict
275+
276+
Dict.fromList [ ( "expenses", 38.25 ), ( "assets", 100.85 ) ]
277+
|> upsert "expenses" 4.50 (\amount -> amount + 2.50)
278+
|> upsert "liabilities" 2.50 (\amount -> amount - 2.50)
279+
--> Dict.fromList [ ( "expenses", 40.75 ), ( "assets", 100.85 ), ( "liabilities", 2.50 ) ]
280+
281+
-}
282+
upsert : comparable -> a -> (a -> a) -> Dict comparable a -> Dict comparable a
283+
upsert key value f dict =
284+
case Dict.get key dict of
285+
Just oldValue ->
286+
Dict.insert key (f oldValue) dict
287+
288+
Nothing ->
289+
Dict.insert key value dict
290+
291+
246292
{-| Keep a key-value pair if its key appears in the set.
247293
248294
import Dict
@@ -337,6 +383,31 @@ invert dict =
337383
dict
338384

339385

386+
{-| Like `invert`, it changes the keys and values. However, if one value maps to multiple keys, then all of the keys will be retained.
387+
388+
import Dict
389+
import Set
390+
391+
Dict.fromList [ ( 1, "Jill" ), ( 2, "Jill" ), ( 3, "Jack" ) ]
392+
|> invertAll
393+
--> Dict.fromList [ ( "Jill", Set.fromList [ 1, 2 ] ), ( "Jack", Set.singleton 3 ) ]
394+
395+
-}
396+
invertAll : Dict comparable1 comparable2 -> Dict comparable2 (Set comparable1)
397+
invertAll dict =
398+
Dict.foldl
399+
(\k v acc ->
400+
case Dict.get v acc of
401+
Just set ->
402+
Dict.insert v (Set.insert k set) acc
403+
404+
Nothing ->
405+
Dict.insert v (Set.singleton k) acc
406+
)
407+
Dict.empty
408+
dict
409+
410+
340411
{-| Determine if any key/value pair satisfies some test.
341412
342413
import Dict

tests/DictTests.elm

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module DictTests exposing (suite)
2+
3+
import Dict
4+
import Dict.Extra
5+
import Expect
6+
import Fuzz exposing (Fuzzer)
7+
import Set
8+
import Set.Extra
9+
import Test exposing (Test, describe)
10+
11+
12+
dictFuzzer : Fuzzer comparable -> Fuzzer v -> Fuzzer (Dict.Dict comparable v)
13+
dictFuzzer keyFuzzer valueFuzzer =
14+
Fuzz.map Dict.fromList (Fuzz.list (Fuzz.pair keyFuzzer valueFuzzer))
15+
16+
17+
suite : Test
18+
suite =
19+
describe "Dict.Extra"
20+
[ describe "invertAll"
21+
[ Test.fuzz (dictFuzzer Fuzz.string Fuzz.int) "does not loose information" <|
22+
\dict ->
23+
dict
24+
|> Dict.Extra.invertAll
25+
|> Dict.map (always Set.size)
26+
|> Dict.values
27+
|> List.sum
28+
|> Expect.equal (Dict.size dict)
29+
, Test.fuzz (dictFuzzer Fuzz.string Fuzz.int) "the values are keys into the original dictionary" <|
30+
\dict ->
31+
dict
32+
|> Dict.Extra.invertAll
33+
|> Dict.Extra.all
34+
(\key vals ->
35+
Set.Extra.all (\val -> Dict.get val dict == Just key) vals
36+
)
37+
|> Expect.equal True
38+
, Test.fuzz (Fuzz.map Dict.Extra.invert (dictFuzzer Fuzz.string Fuzz.int)) "behaves the same as invert on an already inverted dictionary" <|
39+
\dict ->
40+
dict
41+
|> Dict.Extra.invertAll
42+
|> Expect.equal (dict |> Dict.Extra.invert |> Dict.map (always Set.singleton))
43+
]
44+
]

0 commit comments

Comments
 (0)