Skip to content

Commit 2439587

Browse files
author
Ed Flanagan
committed
Add Map.intersect
1 parent bc0cf0b commit 2439587

File tree

3 files changed

+73
-1
lines changed

3 files changed

+73
-1
lines changed

lib/elixir/lib/map.ex

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,46 @@ defmodule Map do
11491149
end
11501150
end
11511151

1152+
@doc """
1153+
Intersects two maps into one.
1154+
1155+
If a key exists in both maps, the value in `map2` will be used.
1156+
1157+
Inlined by the compiler.
1158+
1159+
## Examples
1160+
1161+
iex> Map.intersect(%{a: 1, b: 2}, %{b: "b", c: "c"})
1162+
%{b: "b"}
1163+
1164+
"""
1165+
@doc since: "1.15.0"
1166+
@spec intersect(map, map) :: map
1167+
defdelegate intersect(map1, map2), to: :maps
1168+
1169+
@doc """
1170+
Intersects two maps into one, resolving conflicts through the given `fun`.
1171+
1172+
The given function will be invoked when there are duplicate keys; its
1173+
arguments are `key` (the duplicate key), `value1` (the value of `key` in
1174+
`map1`), and `value2` (the value of `key` in `map2`). The value returned by
1175+
`fun` is used as the value under `key` in the resulting map.
1176+
1177+
Inlined by the compiler.
1178+
1179+
## Examples
1180+
1181+
iex> Map.intersect(%{a: 1, b: 2}, %{b: 2, c: 3}, fn _k, v1, v2 ->
1182+
...> v1 + v2
1183+
...> end)
1184+
%{b: 4}
1185+
"""
1186+
@doc since: "1.15.0"
1187+
@spec merge(map, map, (key, value, value -> value)) :: map
1188+
def intersect(map1, map2, fun) when is_function(fun, 3) do
1189+
:maps.intersect_with(fun, map1, map2)
1190+
end
1191+
11521192
@doc false
11531193
@deprecated "Use Map.new/2 instead (invoke Map.from_struct/1 before if you have a struct)"
11541194
def map(map, fun) when is_map(map) do

lib/elixir/test/elixir/keyword_test.exs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ defmodule KeywordTest do
2121
end
2222

2323
test "implements (almost) all functions in Map" do
24-
assert Map.__info__(:functions) -- Keyword.__info__(:functions) == [from_struct: 1]
24+
assert Map.__info__(:functions) -- Keyword.__info__(:functions) == [
25+
from_struct: 1,
26+
intersect: 2,
27+
intersect: 3
28+
]
2529
end
2630

2731
test "get_and_update/3 raises on bad return value from the argument function" do

lib/elixir/test/elixir/map_test.exs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,34 @@ defmodule MapTest do
231231
end
232232
end
233233

234+
test "intersect/2 with map literals optimized by the compiler" do
235+
map = %{a: 1, b: 2}
236+
237+
assert Map.intersect(map, %{a: 2}) == %{a: 2}
238+
assert Map.intersect(map, %{c: 3}) == %{}
239+
assert Map.intersect(%{a: 2}, map) == %{a: 1}
240+
assert Map.intersect(%{c: 3}, map) == %{}
241+
242+
assert Map.intersect(%{map | a: 2}, %{a: 3}) == %{a: 3}
243+
assert Map.intersect(%{map | a: 2}, %{b: 3}) == %{b: 3}
244+
assert Map.intersect(%{a: 2}, %{map | a: 3}) == %{a: 3}
245+
assert Map.intersect(%{a: 2}, %{map | b: 3}) == %{a: 1}
246+
247+
assert Map.intersect(map, %{a: 2}) |> Map.intersect(%{a: 3, c: 3}) == %{a: 3}
248+
assert Map.intersect(map, %{c: 3}) |> Map.intersect(%{c: 4}) == %{}
249+
assert Map.intersect(map, %{a: 3, c: 3}) |> Map.intersect(%{a: 2}) == %{a: 2}
250+
end
251+
252+
test "intersect/3" do
253+
# When first map is bigger
254+
assert Map.intersect(%{a: 1, b: 2, c: 3}, %{c: 4, d: 5}, fn :c, 3, 4 -> :x end) ==
255+
%{c: :x}
256+
257+
# When second map is bigger
258+
assert Map.intersect(%{b: 2, c: 3}, %{a: 1, c: 4, d: 5}, fn :c, 3, 4 -> :x end) ==
259+
%{c: :x}
260+
end
261+
234262
test "implements (almost) all functions in Keyword" do
235263
assert Keyword.__info__(:functions) -- Map.__info__(:functions) == [
236264
delete: 3,

0 commit comments

Comments
 (0)