Kevin Tanudjaja
Kevin Tanudjaja

Reputation: 886

How to map list using value from another list 1 by 1 in Elixir?

Suppose I have a Player Struct inside list

[
  %Player{name: "John", role: "nil"},
  %Player{name: "Sansa", role: "nil"},
  %Player{name: "Barry", role: "nil"},
  %Player{name: "Edward", role: "nil"}
]

and I have a list of roles:

 Enum.shuffle([:werewolf, :farmer, :farmer, :farmer])

What function to use || How do I map it one by one into my expected result:

[
  %Player{name: "John", role: ":farmer"},
  %Player{name: "Sansa", role: ":farmer"},
  %Player{name: "Barry", role: ":werewolf"},
  %Player{name: "Edward", role: ":farmer"}
]

I tried mapping, but with OO background, all I think is matching the index, which is not efficient in Elixir.

Upvotes: 0

Views: 142

Answers (2)

Adam Millerchip
Adam Millerchip

Reputation: 23091

You could take a different approach: make them all farmers, and randomly assign one as the warewolf:

defmodule Player do
  defstruct name: nil, role: :farmer

  def setup do
    [werewolf | farmers] =
      ["John", "Sansa", "Barry", "Edward"]
      |> Enum.map(fn name -> %Player{name: name} end)
      |> Enum.shuffle()

    [%Player{werewolf | role: :werewolf} | farmers]
  end
end

This will return the players in a random order, though (and the werewolf will always be first).

Upvotes: 1

sabiwara
sabiwara

Reputation: 3159

In general, when you want to somehow work with two lists on the same size and do something element-wise, Enum.zip/2 or Enum.zip_with/3 are a common and efficient way to achieve it without relying on index access.

Your example can be solved by:

Enum.zip_with(players, roles, fn player, role -> Map.put(player, :role, role) end)

Upvotes: 3

Related Questions