Returning value of map in function

I have a function like this:

  def get_captain(team_groups) do
    flatten_members =
      for %{members: members, ratings: ratings} <- team_groups,
          # Zipping will create binary tuples from 2 lists
          {id, rating} <- Enum.zip(members, ratings),
          # Create result value
          do: %{member_id: id, rating: rating}

    captain =
      Enum.sort_by(flatten_members, fn x -> x.rating end, &>=/2)
      |> Enum.at(0)

    captain.member_id
  end

If I change the last line to this:

Enum.sort_by(flatten_members, fn x -> x.rating end, &>=/2)
      |> Enum.at(0).member_id

It doesn’t work. captain is a map with member_id as a key. Why doesn’t that work?

You should replace the sorting and getting the first element by simply invoking Enum.max_by:

    captain = Enum.max_by(flatten_members, fn x -> x.rating end)
2 Likes

dot (.) call happens before pipe (|>) call, so this one:

list |> Enum.at(0).key

is seen like list |> (Enum.at(0).key)

What you need to do is to add another pipe like:

result |> Map.fetch!(:key)
# or
result |> then(& &1.key)

If you have no idea how the code is seen by compiler then check the output of quote call:

iex> quote do
iex>   [%{a: 5}] |> Enum.at(0)
iex> end
{:|>, [context: Elixir, imports: [{2, Kernel}]],
 [
   [{:%{}, [], [a: 5]}],
   # keep in mind this fragment
   {{:., [], [{:__aliases__, [alias: false], [:Enum]}, :at]}, [], [0]}
 ]}

iex> quote do
iex>   [%{a: 5}] |> Enum.at(0).a
iex> end
{:|>, [context: Elixir, imports: [{2, Kernel}]],
 [
   [{:%{}, [], [a: 5]}],
   {{:., [],
     # it's here i.e. nested within a dot call
     [{{:., [], [{:__aliases__, [alias: false], [:Enum]}, :at]}, [], [0]}, :a]},
    [no_parens: true], []}
 ]}
3 Likes

Also worth mentioning that you are building a sorted list just to get the maximum element, which is a bit wasteful.

Enum.max_by/3 should be both more readable and efficient:

Enum.max_by(flatten_members, fn x -> x.rating end).member_id
3 Likes