Bitwise
Bitwise

Reputation: 8461

Select between two maps based on values

I have this dataset:

%{
  "away" => %{
    "id" => "575ec304-wlk3-239n-3032jdns3944",
    "points" => 115
  },
  "home" => %{
    "id" => "583ec7cd-fb46-11e1-82cb-f4ce4684ea4c",
    "points" => 120
  }
}

I want to return the map with the most points so that I end up with this.

%{
  "home" => %{
    "id" => "583ec7cd-fb46-11e1-82cb-f4ce4684ea4c",
    "points" => 120
  }
}

I was thinking the enum.find would work for this but I haven't had any luck. Any help would be great.

Upvotes: 1

Views: 113

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

While Enum.max_by/4 might seem a perfect fit here, the most performant solution would be to use plain old good Enum.reduce/3 with guarded clauses.

input = %{ ... }

Enum.reduce(input, nil, fn 
  {_, %{"points" => pe}}, {_, %{"points" => pa}} = acc when pa > pe -> acc
  e, _ -> e
end)
#⇒ {"home", %{"id" => "583ec7cd-fb46-11e1-82cb-f4ce4684ea4c", "points" => 120}}

To get back from tuple to map, one might wrap the result with List.wrap/1 and Map.new/1

input
|> Enum.reduce(nil, fn 
  {_, %{"points" => pe}}, {_, %{"points" => pa}} = acc when pa > pe -> acc
  e, _ -> e
end)
|> List.wrap()
|> Map.new()
#⇒ %{"home" => %{"id" => "583ec7cd-fb46-11e1-82cb-f4ce4684ea4c", "points" => 120}}

Upvotes: 1

Jason Axelson
Jason Axelson

Reputation: 4685

You want to use Enum.max_by/4 for this because you don't need a specific property of the item you're searching for, but instead you want the item itself with the most points.

iex> data = %{
  "away" => %{
    "id" => "575ec304-wlk3-239n-3032jdns3944",
    "points" => 115
  },
  "home" => %{
    "id" => "583ec7cd-fb46-11e1-82cb-f4ce4684ea4c",
    "points" => 120
  }
}
iex> Enum.max_by(data, fn {_, value} -> value["points"] end)
{"home", %{"id" => "583ec7cd-fb46-11e1-82cb-f4ce4684ea4c", "points" => 120}}

Upvotes: 4

Related Questions