Ilya
Ilya

Reputation: 1150

Elixir/Phoenix exit the loop

I have two lists with maps:

apps = [%{name: "app1", version: "0.0.1"}, %{name: "app2", version: "0.0.1"}]
updates_list = [%{name: "app1", version: "0.0.2"},
                %{name: "app2", version: "0.0.1"},
                %{name: "app3", version: "0.0.1"},
                %{name: "app4", version: "0.0.1"}]

and need to enumerate through the updates_list, to put new key into each element in the apps list to show if there are updates. i.e. Here there is new version for app1, I need to add new_version: true/false and get the desired result:

result = [%{name: "app1", version: "0.0.1", new_version: true},
          %{name: "app2", version: "0.0.1", new_version: false}]

I have tried with the following function:

updates_map = Enum.map(updates_list, fn (x) -> Map.put(%{}, x.name, x.version ) end)
result = Enum.map(apps, fn (x) -> map_updates(x, updates_map) end)

and function:

defp map_updates(app, updates_map) do
  app_map = Map.from_struct(app)
  for update <- updates_map do
    if Map.has_key?(update, app_map.name) do
      if Map.fetch(update, app_map.name) != app_map.version do
        app_map = app_map |> Map.put(:new_version, true)
      else
        app_map = app_map |> Map.put(:new_version, false) 
      end
    else
      app_map = app_map
    end 
  end
  app_map
end

But because it enumerates through all updates_map for every app, it just overrides it, and I get no result. How can exit the loop, or preserve the changes to achieve the result as above? Any advise appreciated!

Upvotes: 0

Views: 1329

Answers (1)

Dogbert
Dogbert

Reputation: 222050

There's no need to write so much code if a O(n*m) solution is acceptable and the criteria for a new version being present is just that an entry with the same name and different version exists in update_map (this is what I think your code does). This can be done with just one for and one nested Enum.any?:

apps = [%{name: "app1", version: "0.0.1"}, %{name: "app2", version: "0.0.1"}]
updates_map = [%{name: "app1", version: "0.0.2"},
               %{name: "app2", version: "0.0.1"},
               %{name: "app4", version: "0.0.1"},
               %{name: "app4", version: "0.0.1"}]

updated_apps = for app <- apps do
  new_version = Enum.any?(updates_map, fn update ->
    app.name == update.name && app.version != update.version
  end)
  Map.put(app, :new_version, new_version)
end

IO.inspect updated_apps

Output:

[%{name: "app1", new_version: true, version: "0.0.1"},
 %{name: "app2", new_version: false, version: "0.0.1"}]

If you need this to run on a large number of apps and updates, you should probably convert the updates into some kind of Map so that the inner logic executes faster.

Upvotes: 2

Related Questions