Reputation: 2055
I'm building a filtering API. Simplifying things, I have something that ressembles a function that takes in a list of structs and a map of filters. My issue is that pairing Enum.filter
with &match?
is not working when passing in the function parameter:
def filter_structs(structs, filters) do
# this always returns []
structs |> Enum.filter(&match?(^filters, &1))
end
But if instead I hardcode the filters, things work fine:
def filter_structs(structs, _filters) do
structs |> Enum.filter(&match?(%{some_field: true}, &1))
end
I have this workaround working, but it's not very pretty... Is there no better solution?
def filter_structs(structs, filters) do
structs
|> Enum.filter(fn s ->
Map.equal?(filters, s |> Map.take(filters |> Map.keys))
end)
end
Upvotes: 2
Views: 639
Reputation: 23091
+1 to sabiwara's answer.
Your version is fine too, I think. Although I'd probably write it without pipes:
def filter_structs(structs, filters) do
keys = Map.keys(filters)
Enum.filter(structs, &(filters === Map.take(&1, keys)))
end
Upvotes: 2
Reputation: 3149
Maps can be pattern matched on a variable key
using %{^key => _}
and on a variable key, value pair using %{^key => ^value}
.
Matching against the whole map using match?(^filters, map)
will only return true
if map === filters
, not contain it.
The following implementation leverages pattern matching and is probably clearer about the intent:
def filter_structs(structs, filters) do
Enum.filter(structs, fn struct ->
Enum.all?(filters, fn {field, value} ->
match?(%{^field => ^value}, struct)
end)
end)
end
Upvotes: 4