Elixir in Action
todo
defmodule TodoList do
def new(), do: %{}
def add_entry(todo_list, date, title) do
Map.update(
todo_list,
date,
[title],
fn titles -> [title | titles] end
)
end
def entries(todo_list, date) do
Map.get(todo_list, date, [])
end
end
defmodule MultiDict do
def new(), do: %{}
def add(dict, key, value) do
Map.update(dict, key, [value], &[value | &1])
end
def get(dict, key) do
Map.get(dict, key, [])
end
end
defmodule TodoListWithMultiDict do
def new(), do: MultiDict.new()
def add_entry(todo_list, date, title) do
MultiDict.add(todo_list, date, title)
end
def entries(todo_list, date) do
MultiDict.get(todo_list, date)
end
end
entry = %{date: ~D[2024-11-29], title: "Elixir in Action"}
IO.puts entry.date
IO.puts entry.title
defmodule TodoListWithMaps do
def new(), do: MultiDict.new()
def add_entry(todo_list, entry) do
MultiDict.add(todo_list, entry.date, entry)
end
def entries(todo_list, date) do
MultiDict.get(todo_list, date)
end
end
todo_list = TodoListWithMaps.new() |>
TodoListWithMaps.add_entry(%{date: ~D[2024-11-29], title: "Groceries"})
todo_list = TodoListWithMaps.add_entry(todo_list, entry)
TodoListWithMaps.entries(todo_list, ~D[2024-11-29])
Abstracting with structs
defmodule Fraction do
defstruct a: nil, b: nil
def new(a, b) do
%Fraction{a: a, b: b}
end
def value(%Fraction{a: a, b: b}) do
a / b
end
def add(%Fraction{a: a1, b: b1}, %Fraction{a: a2, b: b2}) do
new(
a1 * b2 + a2 * b1,
b2 * b1
)
end
end
one_half = %Fraction{a: 1, b: 2}
one_half.a
one_half.b
# pattern matching
%Fraction{a: a, b: b} = one_half
one_quarter = %Fraction{one_half | b: 4}
Fraction.new(1, 2)
|> Fraction.add(Fraction.new(1, 4))
|> Fraction.value()
Map.to_list(one_half)
# A struct pattern can't match a plain map:
# %Fraction{a: a, b: b} = %{a: 1, b: 2}
# However, a plain map pattern can match a struct:
%{a: a, b: b} = %Fraction{a: 1, b: 2}
Working with hierarchical data
defmodule Todo do
defstruct next_id: 1, entries: %{}
def new(), do: %Todo{}
def add_entry(todo_list, entry) do
entry = Map.put(entry, :id, todo_list.next_id)
new_entries = Map.put(
todo_list.entries,
todo_list.next_id,
entry
)
%Todo{todo_list |
entries: new_entries,
next_id: todo_list.next_id + 1
}
end
def entries(todo_list, date) do
todo_list.entries
|> Map.values()
|> Enum.filter(fn entry -> entry.date == date end)
end
def update_entry(todo_list, entry_id, updater_fun) do
case Map.fetch(todo_list.entries, entry_id) do
:error ->
todo_list
{:ok, old_entry} ->
new_entry = updater_fun.(old_entry)
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
%Todo{todo_list | entries: new_entries}
end
end
def delete_entry(todo_list, entry_id) do
case Map.fetch(todo_list.entries, entry_id) do
:error ->
todo_list
{:ok, _} ->
Map.delete(todo_list.entries, entry_id)
end
end
def delete_entry2(todo_list, entry_id) do
%Todo{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
end
end
# push some new todos
todo_list = Todo.new() |>
Todo.add_entry(%{date: ~D[2024-11-29], title: "Elixir in Action"}) |>
Todo.add_entry(%{date: ~D[2024-11-30], title: "Mimoza"}) |>
Todo.add_entry(%{date: ~D[2024-12-01], title: "Hajduk - Dinamo"})
Todo.entries(todo_list, ~D[2024-11-30])
list = %{
1 => %{date: ~D[2023-12-19], title: "Gym"},
2 => %{date: ~D[2023-12-20], title: "Shopping"},
3 => %{date: ~D[2023-12-19], title: "Movies"}
}
put_in(list[3].title, "Theater")
Todo.delete_entry2(todo_list, 1)
defmodule TodoBuilder do
defstruct next_id: 1, entries: %{}
def new(entries \\ []) do
Enum.reduce(
entries,
%TodoBuilder{},
fn entry, todo_list_acc ->
add_entry(todo_list_acc, entry)
end
)
end
def add_entry(todo_list, entry) do
entry = Map.put(entry, :id, todo_list.next_id)
new_entries = Map.put(
todo_list.entries,
todo_list.next_id,
entry
)
%Todo{todo_list |
entries: new_entries,
next_id: todo_list.next_id + 1
}
end
def entries(todo_list, date) do
todo_list.entries
|> Map.values()
|> Enum.filter(fn entry -> entry.date == date end)
end
def update_entry(todo_list, entry_id, updater_fun) do
case Map.fetch(todo_list.entries, entry_id) do
:error ->
todo_list
{:ok, old_entry} ->
new_entry = updater_fun.(old_entry)
new_entries = Map.put(todo_list.entries, new_entry.id, new_entry)
%Todo{todo_list | entries: new_entries}
end
end
def delete_entry(todo_list, entry_id) do
%Todo{todo_list | entries: Map.delete(todo_list.entries, entry_id)}
end
end