mxst
Can I have a devstruct with computed values?
Hey everyone.
I was wondering about how I should initialize a struct where I have some values and some computed values.
For now I came up with:
defmodule Stats do
defstruct annotations: 0, true_positive: 0,
false_positive: 0, false_negative: 0,
precision: 0, recall: 0
def set_precision(stats \\ %AnnotationStats{}) do
Map.put(stats, :precision, (stats.true_positive / (stats.true_positive + stats.false_positive)))
end
def set_recall(stats \\ %AnnotationStats{}) do
Map.put(stats, :recall, (stats.true_positive / (stats.true_positive + stats.false_negative)))
end
end
What I the initialize with
my_stats = %Stats{annotations: 3, true_positive: 1, false_positive: 2, false_negative: 1}
|> set_precision()
|> set_recall()
But is there a cleaner way ? Or the way to do it?
Thank you all 
Marked As Solved
Eiji
Here is my proposition:
defmodule Stats do
required_keys = [:false_negative, :false_positive, :true_positive]
@enforce_keys required_keys
defstruct required_keys ++ [annotations: 0, precision: 0, recall: 0]
@type t :: %__MODULE__{
annotations: integer,
false_negative: integer,
false_positive: integer,
precision: float,
recall: float,
true_positive: integer
}
@spec init(t) :: t
def init(%__MODULE__{true_positive: true_positive} = struct) do
computed = %{
precision: init_computed(true_positive, struct.false_positive),
recall: init_computed(true_positive, struct.false_negative)
}
Map.merge(struct, computed)
end
defp init_computed(true_positive, value), do: true_positive / (true_positive + value)
end
Stats.init(%Stats{annotations: 3, false_negative: 1, false_positive: 2, true_positive: 1})
It would fail if you did not pass 3 keys (specified in @enforce_keys) which are required to compute.
Alternatively you could write something like:
defmodule Stats do
required_keys = [:false_negative, :false_positive, :true_positive]
@enforce_keys required_keys
defstruct required_keys ++ [annotations: 0, precision: 0, recall: 0]
@type t :: %__MODULE__{
annotations: integer,
false_negative: integer,
false_positive: integer,
precision: float,
recall: float,
true_positive: integer
}
@spec init(map, integer, integer, integer) :: t
def init(map, false_negative, false_positive, true_positive) do
full_map =
map
|> Map.put(:false_negative, false_negative)
|> Map.put(:false_positive, false_positive)
|> Map.put(:precision, init_computed(true_positive, false_positive))
|> Map.put(:recall, init_computed(true_positive, false_negative))
|> Map.put(:true_positive, true_positive)
struct(__MODULE__, full_map)
end
defp init_computed(true_positive, value), do: true_positive / (true_positive + value)
end
This way allows to pass map (don’t require struct), but forces to pass required keys separately.
Finally you can also do it in more pattern-matching like way:
defmodule Stats do
required_keys = [:false_negative, :false_positive, :true_positive]
@enforce_keys required_keys
defstruct required_keys ++ [annotations: 0, precision: 0, recall: 0]
@type init_map :: %{
optional(:annotations) => integer,
optional(:precision) => float,
optional(:recall) => float,
required(:false_negative) => integer,
required(:false_positive) => integer,
required(:true_positive) => integer
}
@type t :: %__MODULE__{
annotations: integer,
false_negative: integer,
false_positive: integer,
precision: float,
recall: float,
true_positive: integer
}
@spec init(init_map) :: t
def init(
%{
false_negative: false_negative,
false_positive: false_positive,
true_positive: true_positive
} = map
) do
computed = %{
precision: init_computed(true_positive, false_positive),
recall: init_computed(true_positive, false_negative)
}
__MODULE__ |> struct(map) |> Map.merge(computed)
end
defp init_computed(true_positive, value), do: true_positive / (true_positive + value)
end
Which allows to pass required keys in map.
2
Popular in Questions
In Ruby, I can go:
User.find_by(email: "foobar@email.com").update(email: "hello@email.com")
How can I do something similar in Elixir? ...
New
Credo is smart enough to check for (something like) this:
assert length(the_list) == 0
with this response:
Checking if an enum is empt...
New
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database.
Dep...
New
i’m a new one to elixir
which editor can i use
vs code? or atom?
Thanks! :smiley:
New
What is most correct way to open, read and parse JSON file with poison?
For example if we have example.json file in root of some projec...
New
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
Sometimes I want to check if the input into a function is not a blank string.
My first approach:
defmodule Example do
def do_stuff(s...
New
I am trying to run a deploy with docker and I successfully runned with this command:
docker build -t romenigld/blog-prod .
but when I t...
New
Other popular topics
Manning 2016 Halloween weekend sale via Deal of the Day
Friday, October 28 - Half off all MEAPs - code WM102816LT
Saturday, October 29 ...
New
What is the idiomatic way of matching for not nil in Elixir?
E.g.,
First way:
defp halt_if_not_signed_in(conn, signed_in_account) when...
New
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode.
The solution seems to be, in a hyphena...
New
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
New
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum.
...
New
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors:
[WARN] - (starship::utils): Executing command ...
New







