Reputation: 13
I've been learning the basics of Elixir for the past few days, and while I have read more than once that novices have a hard time deciding which data structures to use, I personally haven't seen anything much different between Maps and Structs. By this I mean:
Map.fetch(@map, :field)
, while on a struct I can easily access it with just a '.' in between. Is there a performance reason to use maps? Or am I blind to some other major reason? Because the need to always call Map.some_function
to deal with maps seems quite more verbose than just going with a struct.
Upvotes: 1
Views: 1350
Reputation: 48599
i personally haven't seen anything much different between Maps and Structs
If you know all the keys that are going to be in your map ahead of time, then use a struct.
If your map needs to store an indeterminate number of keys, use a map. For instance, if you are reading a csv(comma separated values) file, where each line has two values on it (a potential key and a value in a map), but the number of lines varies in the files you are reading, or the keys and values are not the same in the various files, and you want to enter those key/value pairs in a map, then you wouldn't be able to use a struct.
By default, I would choose a map, then if at some later point you realize that the keys are always the same for the maps you are creating, then I would change the code to use a struct.
To confuse things even more, elixir also has keyword lists
, for example:
[{:a, 1}, {:b, 2}]
or the equivalent shorthand syntax:
[a: 1, b: 2]
Elixir provides the Keyword module for accessing values:
...most of the functions in this module behave exactly as a dictionary so they work similarly to the functions you would find in the Map module.
You can use a keyword list when you would like a map to store more than one value for the same key; or when you want the key/value pairs to be in a certain order.
Upvotes: 1
Reputation: 5973
Structs are just maps with a contract around what keys are available. Let's define a struct.
iex> defmodule Alpha do
...> defstruct [:a, :b, :c]
...> end
Then let's use it:
iex> %Alpha{a: 3, b: 4, c: 5}
%Alpha{a: 3, b: 4, c: 5}
Let's create a similar map:
iex> %{a: 3, b: 4, c: 5}
%{a: 3, b: 4, c: 5}
If we just add a :__struct__
key, we suddenly get a struct:
iex> %{__struct__: Alpha, a: 3, b: 4, c: 5}
%Alpha{a: 3, b: 4, c: 5}
The .
syntax is available for any map with atom keys:
iex> user = %{name: "Brett"}
%{name: "Brett"}
iex> user.name
"Brett"
But if you use it for a key not available in the map, you'll get an error:
iex> user.age
** (KeyError) key :age not found in: %{name: "Brett"}
Where structs have a defined set of keys, you can be a lot more confident the key you're interested in is available.
However, there are plenty of situations where you want to use maps in other ways. They allow you a lot more efficient lookups than iterating a list any time you want to find something.
Upvotes: 3
Reputation: 23091
Your assumptions about being required to use the Map
module to interact with maps are incorrect. For example, in iex:
iex(1)> foo = %{bar: "baz"}
%{bar: "baz"}
iex(2)> foo.bar
"baz"
iex(3)> foo = %{foo | bar: "other"}
%{bar: "other"}
Here we create a map, fetch the :bar
key, then update the value from "baz"
to "other"
, all using built-in syntax.
The reason to use structs is for compile-time checking of the keys. For example, if we define a struct with key foo:
defmodule ExampleStruct do
defstruct [:foo]
end
And then try to create it with key bar
:
iex(1)> %ExampleStruct{bar: "baz"}
** (KeyError) key :bar not found
(testy) expanding struct: ExampleStruct.__struct__/1
iex:3: (file)
We get an error, because :bar
is not a valid key for ExampleStruct
. With a map, there are no checks on the keys that can be added.
Upvotes: 4
Reputation: 1377
Struct is just a map under the hood with additional field called __struct__
. When you are creating a map you cannot force the user to include certain fields or provide some default values to some of the fields but you can do that with structs. Beside that you can use structs and protocols to achieve data polymorphism which is function will be invoked depending on the struct type. A sample scenario would be if your application has users on it and you would rather use %User{}
(example below) struct with some enforced fields like name
and surname
instead of relying on a %{}
(map). Through this way you make sure that whenever a user struct is created you will have the name
and surname
fields for sure otherwise you will get compile time error.
defmodule User do
@enforce_keys [:name, :surname]
defstruct [:name, :surname]
end
https://elixir-lang.org/getting-started/structs.html
Upvotes: 1