0

I'm new to Elixir. Currently using a Pheonix framework. I need to convert structs to list so I can use them in the select field.

In my user_controller.ex I attempt to convert a struct to list, but the variable user_types_name is always empty.

user_type_names = []
for user <- Accounts.list_user_types() do
     user_type_names ++ [user.name]
end

While in form.html.eex. For additional information

<%= inputs_for f, :user_type, fn cf -> %>
    <%= label cf, :user_type %>
    <%= select cf, :user_type, @user_type_names %>
    <%= error_tag cf, :user_type %>
<% end %>
NJY404
  • 349
  • 3
  • 14

3 Answers3

2

All data in Elixir is immutable. ++ is a concatenation operator, not a way to mutate a list by pushing an item onto it.

You'll need to use something like this:

user_type_names = Accounts.list_user_types()
  |> Enum.map(fn(user) -> user.name end)

Or, more concisely:

user_type_names = Accounts.list_user_types()
  |> Enum.map(&(&1.name))
Lionel Rowe
  • 5,164
  • 1
  • 14
  • 27
  • I used your first suggestion and it worked, thanks. But I'm a bit lost when you used ```Enum.map(&(&1.name))```, can you elaborate it to me? – NJY404 Aug 18 '20 at 22:39
  • 1
    @NJY404 see here: [What does "&1" mean in elixir function?](https://stackoverflow.com/questions/55067455/what-does-1-mean-in-elixir-function) – Lionel Rowe Aug 19 '20 at 05:19
1

You should perhaps look at Enum.map

Enum.map(1..3, fn x -> x * 2 end)
[2, 4, 6]

Enum.map returns a list by itself, so you might want do something like

user_type_names = Enum.map(Accounts.list_user_types()...)

and this should return a list.

Documentation is pretty well written : https://hexdocs.pm/elixir/Enum.html

Elixir forces you to think about what module ( Enum ) and what function ( map ) you are going to use. Think functional is the key to make things work

Andrei R
  • 11
  • 3
1

In Elixir, for is a construct for comprehensions. Meaning that the result will be a list built with the result of the do clause for each element in the enumerables you use.

So, in your example, you are in fact building a list where each element is the concatenation of user_type_names (which every time is the empty list you assigned) with the current user.name.

What was suggested about assigning won't really work inside the for's do clause, because of the scoping rules. You will be reassigning it but the result will actually be the same, because it will still be the result of the match (=).

The solution is actually far simpler than what you're doing. It's enough to do:

user_type_names = for user <- Accounts.list_user_types(), do: user.name
sbacarob
  • 2,092
  • 1
  • 14
  • 19