Creating custom struct/map and adding to list issue

Hey here is what I am trying to do:
we have this list of structs or maps that is given by get_teams:

[
  %Userteam1.Web.Team{
__meta__: #Ecto.Schema.Metadata<:loaded, "teams">,
id: 1,
inserted_at: ~N[2018-09-06 16:53:47.287643],
name: "testteam1",
updated_at: ~N[2018-09-06 16:53:47.287652],
users: #Ecto.Association.NotLoaded<association :users is not loaded>
  },
  %Userteam1.Web.Team{
__meta__: #Ecto.Schema.Metadata<:loaded, "teams">,
id: 2,
inserted_at: ~N[2018-09-07 07:42:14.333715],
name: "dfs",
updated_at: ~N[2018-09-07 07:42:14.333737],
users: #Ecto.Association.NotLoaded<association :users is not loaded>
  }
]

But i need a different struct like this:

team_new = %{
    name: team.name,
    team_score: team_score
  }

I am not sure if I am going right about this, but what I am trying to do is this:

def index(conn, _params) do
    teams = Web.list_teams()
    IO.inspect(teams)
    teams_with_scores = []

    for team <- teams do
      team_score = get_team_score(team)

      team_new = %{
        name: team.name,
        team_score: team_score
      }

      IO.inspect(team_new)
      internal_list = [team_new]
      teams_with_scores = [internal_list | teams_with_scores]
    end

    IO.inspect(teams_with_scores)

    render(conn, "index.json", teams: teams)
  end

So what I am trying to do is to make my own struct with the data i need here, and it is created nicely, but how do i shape it into the form that i showed at the start of the post?
That it is inside a list?
How do I add to it all?

when i print that list out here is stays empty, and the console says this when i start to run the app:

warning: variable "teams_with_scores" is unused

Note variables defined inside case, cond, fn, if and similar do not leak. If you want to conditionally override an existing variable "teams_with_scores", you will have to explicitly return the variable. For example:

if some_condition? do
  atom = :one
else
  atom = :two
end

should be written as

atom =
  if some_condition? do
    :one
  else
    :two
  end

So what i understand form this is that this:

is not able to to what I want, but I don’t understand how to make it so, what does the console’s message mean?

for does not leak inner bindings to the outer world, thats why teams_with_scores is not available outside.

As you have it now, your code is free of side effects (ignoring the inspect which is for debugging only, I assume), so you could remove the for without changing the observable behaviour of your program.

If I understand you correctly, what youz actually want is this:

# with comprehension
teams_with_scores = for team <- teams do
  %{name: team.name, team_score: get_team_score(team)}
end

# using `Enum.map/2`
teams_with_scores = Enum.map(teams, fn team ->
  %{name: team.name, team_score: get_team_score(team)}
end

Just replace everything of your code beginning with teams_with_scores = [] and endingwith the IO.inspect(teams_with_scores) with one of my suggestions.

1 Like
for team <- teams do

This already creates a list for you. The last statement of that do block will be added to the created list.

Example:

for x <- [1, 2, 3] do
  temp = x * 3
  String.duplicate(".", temp)
end

gives as output:

["...", "......", "........."]

Note that there was no list explicitly created: that’s what the for construct does: run the do block for every entry (in your case, each %Userteam1.Web.Team struct) and puts the results from each of those runs into a list.

See the docs on list comprehensions for more info.

This is because teams_with_scores is never used, as it notes. It is assigned at the end of the do block, but nothing is done with it. Remember that the last statement in any block becomes, implicitly, the return value. So in this case it assigns to teams_with_scores and then returns that same value, but never actually uses the variable.

Because it is in its own do block, that teams_with_scores is local to the for’s do block. It has nothing to do with the teams_with_scores at the top of index/2. So the compiler is hinting at this with its note about leakage. Leaking is when a variable jumps scope; generally never desirable!

This is one of the differences between loosey-goosey-do-whatever-with-unpredictable-at-times-results imperative languages and functional (or even just more strictly scoped) languages like Elixir.

So you just want something like:

teams_with_scores =
  for team <- teams, do: % { name: team.name, team_score: get_team_score(team)}

Voila.

2 Likes

Wow, I am really impressed by how clean it is!
Thank you for the both of you!