We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Advanced Functional Elixir - 02 - Act on It
Published on: 2026-03-05
Tags:
Blog, Side Project, Advanced Functional Programming, FunPark, Act on It
Act on It
Create a contra_map/1 that normalizes strings before comparing them - make sure that they are trimmed and use the same case.
### FunPark.Eq.Utils
@doc """
Build an equality map that first normalizes strings before comparing.
The provided function will trim whitespace and downcase binaries so that
comparisons are case-insensitive and ignore surrounding spaces. The
returned map can be passed to `contramap/2` or used directly with the
helpers `eq?/3` and `not_eq?/3`.
"""
def contramap_string(eq \\ Eq) do
contramap(&normalize_string/1, eq)
end
defp normalize_string(str) when is_binary(str) do
str
|> String.trim()
|> String.downcase()
end
defp normalize_string(other), do: other
Now that I have that we can test with a few values.
iex> eq_map = FunPark.Eq.Utils.contramap_string()
%{eq?: #Function<…>, not_eq?: #Function<…>}
iex> FunPark.Eq.Utils.eq?(" Hello ", "hello", eq_map)
true
iex> FunPark.Eq.Utils.eq?("Hello", "HELLO", eq_map)
true
Now to push this even further I wanted to and an other that tests integers. I picked so long as the integer value above the hundreds is the same (1120 == 1180) it will evaluate to true
@doc """
Build a map that first normalizes an integer before comparing.
This will make sure that as long as the values above the 100's digit is
the same it will evaluate to the equal. It will also make sure that if
the value is less than 100 it will always be the same.
"""
def contramap_integer(eq \\ Eq) do
contramap(&normalize_integer/1, eq)
end
defp normalize_integer(int) when is_integer(int) do
hundreds = rem(int, 100)
int - hundreds
end
defp normalize_integer(other), do: other
Now here are a few tests.
iex(46)> FunPark.Eq.Utils.eq?(1000, 1020)
false
iex(47)> eq_int_map = FunPark.Eq.Utils.contramap_integer()
%{
eq?: #Function<0.114186896/2 in FunPark.Eq.Utils.contramap/2>,
not_eq?: #Function<1.114186896/2 in FunPark.Eq.Utils.contramap/2>
}
iex(48)> FunPark.Eq.Utils.eq?(1000, 1020, eq_int_map)
true
iex(49)> FunPark.Eq.Utils.eq?(1080, 1020, eq_int_map)
true
A Patron wants to give their FastPasses to a friend. How would you check that none of the friends pass are for the same time?
Let’s assume the following passes.
iex(50)> pass_a = FunPark.FastPass.make(tea_cup, datetime)
%FunPark.FastPass{
id: 1230,
ride: %FunPark.Ride{
id: 1090,
name: "Tea Cup",
min_age: 0,
min_height: 0,
wait_time: 0,
online: true,
tags: []
},
time: ~U[2025-06-01 13:00:00Z]
}
iex(51)> pass_b = FunPark.FastPass.make(mansion, datetime)
%FunPark.FastPass{
id: 1294,
ride: %FunPark.Ride{
id: 1026,
name: "Dark Mansion",
min_age: 14,
min_height: 0,
wait_time: 0,
online: true,
tags: [:dark]
},
time: ~U[2025-06-01 13:00:00Z]
}
iex(52)> datetime_2 = DateTime.new!(~D[2025-06-01], ~T[13:01:01])
~U[2025-06-01 13:01:01Z]
iex(53)> pass_c = FunPark.FastPass.make(mansion, datetime_2)
%FunPark.FastPass{
id: 1486,
ride: %FunPark.Ride{
id: 1026,
name: "Dark Mansion",
min_age: 14,
min_height: 0,
wait_time: 0,
online: true,
tags: [:dark]
},
time: ~U[2025-06-01 13:01:01Z]
}
iex(54)> datetime_3 = DateTime.new!(~D[2025-06-01], ~T[13:11:01])
~U[2025-06-01 13:11:01Z]
iex(55)> pass_d = FunPark.FastPass.make(haunted_mansion, datetime_3)
%FunPark.FastPass{
id: 1678,
ride: %FunPark.Ride{
id: 578,
name: "Haunted Mansion",
min_age: 0,
min_height: 0,
wait_time: 0,
online: true,
tags: []
},
time: ~U[2025-06-01 13:11:01Z]
}
iex(56)> jims_passes = [pass_a, pass_c, pass_d]
[
%FunPark.FastPass{
id: 1230,
ride: %FunPark.Ride{
id: 1090,
name: "Tea Cup",
min_age: 0,
min_height: 0,
wait_time: 0,
online: true,
tags: []
},
time: ~U[2025-06-01 13:00:00Z]
},
%FunPark.FastPass{
id: 1486,
ride: %FunPark.Ride{
id: 1026,
name: "Dark Mansion",
min_age: 14,
min_height: 0,
wait_time: 0,
online: true,
tags: [:dark]
},
time: ~U[2025-06-01 13:01:01Z]
},
%FunPark.FastPass{
id: 1678,
ride: %FunPark.Ride{
id: 578,
name: "Haunted Mansion",
min_age: 0,
min_height: 0,
wait_time: 0,
online: true,
tags: []
},
time: ~U[2025-06-01 13:11:01Z]
}
]
iex(57)> sals_passes = [pass_b]
[
%FunPark.FastPass{
id: 1294,
ride: %FunPark.Ride{
id: 1026,
name: "Dark Mansion",
min_age: 14,
min_height: 0,
wait_time: 0,
online: true,
tags: [:dark]
},
time: ~U[2025-06-01 13:00:00Z]
}
]
If Jim wants to give sal his passes we should only be able to give him pass_c, and pass_d. As a and b share the same time.
How can we check to see if the passes are the same (time wise)
iex(58)> no_clashes? =
...(58)> FunPark.List.difference(sals_passes, jims_passes, FunPark.FastPass.eq_time()) == sals_passes
false
iex(59)> FunPark.List.difference(sals_passes, jims_passes, FunPark.FastPass.eq_time())
[]
iex(60)> FunPark.List.difference(jims_passes, sals_passes, FunPark.FastPass.eq_time())
[
%FunPark.FastPass{
id: 1486,
ride: %FunPark.Ride{
id: 1026,
name: "Dark Mansion",
min_age: 14,
min_height: 0,
wait_time: 0,
online: true,
tags: [:dark]
},
time: ~U[2025-06-01 13:01:01Z]
},
%FunPark.FastPass{
id: 1678,
ride: %FunPark.Ride{
id: 578,
name: "Haunted Mansion",
min_age: 0,
min_height: 0,
wait_time: 0,
online: true,
tags: []
},
time: ~U[2025-06-01 13:11:01Z]
}
]