wintermeyer
wintermeyer

Reputation: 8318

Cascade Enum.reduce/3 and Map.update/4

In my quest to find a good solution for List possible train routes on a given model railway I try to tackle this:

# This is the data I have.
#
trains = [:red_train, :blue_train]
train_stations = ["Station 1", "Station 2"]

# This stepping stone is calculated by a function.
#
stations_permutations = [
    ["Station 1", "Station 2"], 
    ["Station 2", "Station 1"]
]

# This should be the result.
#
possible_routes = 
    [
    %{
        end: %{blue_train: "Station 2", red_train: "Station 1"},
        start: %{blue_train: "Station 2", red_train: "Station 1"}
    },
    %{
        end: %{blue_train: "Station 1", red_train: "Station 2"},
        start: %{blue_train: "Station 1", red_train: "Station 2"}
    },
    %{
        end: %{blue_train: "Station 1", red_train: "Station 2"},
        start: %{blue_train: "Station 2", red_train: "Station 1"}
    },
    %{
        end: %{blue_train: "Station 2", red_train: "Station 1"},
        start: %{blue_train: "Station 1", red_train: "Station 2"}
    }
    ]

My problem: I want to write a function list_routes/2 which has trains and train_stations as parameter. It should return with a list of maps (one start and one end) which represent all possible routes on the given model railway. I'm mentally stuck in OO Ruby land and can not figure out how to solve this in a functional way.

I want to run through all station_permutations and match them to the trains. Once for start and once for end.

In Ruby I would do that with a loop. In Elixir that doesn't work. I guess that Enum.reduce and Map.update is the way to go. But is it and how can I cascade those?

def station_permutations(_train_stations) do
  [
    ["Station 1", "Station 2"], 
    ["Station 2", "Station 1"]
  ]
end

@doc """
Returns a list of all possible routes.

## Examples
    iex> Trains.list_routes([:red_train, :blue_train], ["Station 1", "Station 2"])
    [
      %{
        end: %{blue_train: "Station 2", red_train: "Station 1"},
        start: %{blue_train: "Station 2", red_train: "Station 1"}
      },
      %{
        end: %{blue_train: "Station 1", red_train: "Station 2"},
        start: %{blue_train: "Station 1", red_train: "Station 2"}
      },
      %{
        end: %{blue_train: "Station 1", red_train: "Station 2"},
        start: %{blue_train: "Station 2", red_train: "Station 1"}
      },
      %{
        end: %{blue_train: "Station 2", red_train: "Station 1"},
        start: %{blue_train: "Station 1", red_train: "Station 2"}
      }
    ]
"""
def list_routes(trains, train_stations) do
  station_permutations(train_stations)
  |> Enum.reduce(%{}, fn stations, acc ->
    Map.update(acc, stations, nil, ???????)
  end)
end

Upvotes: 0

Views: 41

Answers (1)

lud
lud

Reputation: 2001

First I would compute all possible states of the trains:

possible_states = Enum.map(stations_permutations, &Enum.zip(trains, &1))

Then I would loop over those states, in a double loop, one for the starting state, the other for the ending state:

for state_start <- possible_states, state_end <- possible_states do
  %{start: state_start, end: state_end}
end

If you really want to use reduce for learning purposes then:

Enum.reduce(possible_states, [], fn state_start, acc ->
  Enum.reduce(possible_states, acc, fn state_end, acc ->
    [%{start: state_start, end: state_end} | acc]
  end)
end)

Note the initial accumulator for the outer loop is [] whereas the inner loop receives the current accumulator acc.

Upvotes: 1

Related Questions