# Assign members of a list into two lists

I have an input that is a list of players:

``````  [
%{rating: 8, member_id: 1},
%{rating: 5, member_id: 4},
%{rating: 6, member_id: 2},
%{rating: 7, member_id: 3}
]
``````

I then want to divide them into two teams that would look like this:

``````[%{team_id: 0, members: []}, %{team_id: 1, members: []}]
``````

The team with the least amount of players picks first; if there is a tie then the team with the lowest summed rating of members picks. The team will always pick the first in the player list that hasn’t already been picked.

So team 1 would pick member id 1, then team 2 would pick member id 4, then team 2 would pick member id 2, then team 1 would pick member id 3.

My first thought is to use Map.reduce with

``````[%{team_id: 0, members: []}, %{team_id: 1, members: []}]
``````

as the accumulator. But not sure how to update it.

You can do in multiple ways:

## Plain pattern matching

This works only on fixed length lists, technically it can be applied for longer lists, but that requires some additional steps.

``````[a0, a1, b0, b1] = list

[%{team_id: 0, members: [a0, b0]}, %{team_id: 1, members: [a1, b1]}]
``````

## Using `Enum.reduce/3`

``````{team0, team1} = Enum.reduce(list, {[], []}, fn member, {curr, next} ->
{next, [member | curr]}
end)

[%{team_id: 0, members: team0}, %{team_id: 1, members: team1}]
``````

Though there `team0` and `team1` members will be in reverse order. You can call `Enum.reverse/1` on these to change that.

## `Enum.group_by/3`

``````list
|> Enum.with_index()
|> Enum.group_by(fn {x, _} -> rem(x, 2) end, fn {_, team} -> team end)
|> Enum.map(fn {id, members} -> %{team_id: id, members: members} end)
``````

There probably are some more ways to do so, but it is getting late there and I do not want to think too much about these.

You’d need at least one other piece of “state” in the accumulator of `reduce` - which team is picking (0 or 1).

HOWEVER

`reduce` is powerful, but also complicated. Using more-specific functions from `Enum` may be more readable. For instance:

``````input = [
%{rating: 8, member_id: 1},
%{rating: 5, member_id: 4},
%{rating: 6, member_id: 2},
%{rating: 7, member_id: 3}
]

{members0, members1} =
input
|> Enum.chunk_every(2) # produces lists with 2 players, except if there's leftover at the end
|> Enum.map(fn
[p0, p1] -> {p0, p1}
[p0] -> {p0, nil}
end)
|> Enum.unzip()

[%{team_id: 0, members: members0}, %{team_id: 1, members: members1}]
``````

I ended up writing some code

``````  @doc """
member_list: A sorted list of members e.g.
[
%{rating: 8, member_id: 1},
%{rating: 5, member_id: 4},
%{rating: 6, member_id: 2},
%{rating: 7, member_id: 3}
]
"""
def assign_teams(member_list) do
Enum.reduce(member_list, [%{team_id: 0, members: []}, %{team_id: 1, members: []}], fn x,
acc ->
picking_team = get_picking_team(acc)
update_picking_team = Map.merge(picking_team, %{members: [x | picking_team.members]})
[update_picking_team | get_non_picking_teams(acc, picking_team)]
end)
end

def get_picking_team(teams) do
default_picking_team = Enum.at(teams, 0)

Enum.reduce(teams, default_picking_team, fn x, acc ->
# Team is picker if it has least members
if(length(x.members) < length(acc.members)) do
x
else
# Team is picker if it is tied for least and has lower team rating
if(
length(x.members) == length(acc.members) && get_team_rating(x) < get_team_rating(acc)
) do
x
else
acc
end
end
end)
end

def get_non_picking_teams(teams, picking_team) do
Enum.filter(teams, fn x -> x.team_id != picking_team.team_id end)
end

def get_team_rating(team) do
Enum.reduce(team.members, 0, fn x, acc ->
acc + x.rating
end)
end
``````

Feel free to suggest any improvements.

Cache `length/1` if you need to use it repeatedly, because that function is `O(n)`

1 Like