Last active
December 21, 2019 12:14
-
-
Save boydm/ab7aec16d54709862ec3491c2b01ddda to your computer and use it in GitHub Desktop.
Elixir meyers_difference for Maps
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Boydm.Utilities.Map do | |
#============================================================================ | |
# similar to List.meyers_difference, the below code compares two maps | |
# and generates a list of actions that can be applied to the first map | |
# to transform it into the second map. | |
#-------------------------------------------------------- | |
def difference(map_1, map_2) when is_map(map_1) and is_map(map_2) do | |
# remove any keys from map_1 that are simply not present (at all) in map_2 | |
difference = Enum.reduce(map_1, [], fn({k,_}, d)-> | |
case Map.has_key?(map_2, k) do | |
false -> [{:del, k} | d] | |
true -> d | |
end | |
end) | |
# add any puts for keys that have changed between map_2 to map_1 | |
Enum.reduce(map_2, difference, fn({k,v}, d)-> | |
case Map.has_key?(map_1, k) && (Map.get(map_1, k) == v) do | |
false -> [{:put, k, v} | d] | |
true -> d | |
end | |
end) | |
end | |
#-------------------------------------------------------- | |
def apply_difference(map, difference) when is_map(map) and is_list(difference) do | |
Enum.reduce(difference, map, fn(diff, acc)-> | |
case diff do | |
{:put, k, v} -> Map.put(acc, k, v) | |
{:del, k} -> Map.delete(acc, k) | |
end | |
end) | |
end | |
end |
Here is another (non-golfed) version that lets you invert the diffs by keeping track of the initial values. This way you can apply the diff either way like such:
diff = difference(m1, m2)
apply(m1, diff) === m2 # true
apply(m2, invert(diff)) === m1 # true
def difference(m1, m2) when is_map(m1) and is_map(m2) do
difference =
for {k, v} <- m1,
not Map.has_key?(m2, k),
do: {:del, k, v}
Enum.reduce m2, difference, fn {k, v}, diff ->
if not Map.has_key?(m1, k) do
[{:new, k, v} | diff]
else
case Map.get(m1, k) do
^v -> diff
old_v -> [{:update, k, old_v, v} | diff]
end
end
end
end
def apply(map, difference) when is_map(map) and is_list(difference) do
Enum.reduce difference, map, fn
{:new, k, v}, map -> Map.put(map, k, v)
{:update, k, _old_v, v}, map -> Map.put(map, k, v)
{:del, k, _old_v}, map -> Map.delete(map, k)
end
end
def invert(diff) when is_list(diff) do
Enum.map diff, fn
{:new, k, v} -> {:del, k, v}
{:update, k, from, to} -> {:update, k, to, from}
{:del, k, v} -> {:new, k, v}
end
end
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here is a golfed version, just for fun.