hanweir
hanweir

Reputation: 13

Why would I use maps in Elixir

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:

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

Answers (4)

7stud
7stud

Reputation: 48599

i personally haven't seen anything much different between Maps and Structs

  1. If you know all the keys that are going to be in your map ahead of time, then use a struct.

  2. 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

Brett Beatty
Brett Beatty

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

Adam Millerchip
Adam Millerchip

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

Tano
Tano

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

Related Questions