And now I would like to get only the plans that have a category inside this list selected_categories = [“basketball”]
** The list can have 1 or more categories inside, for example selected_categories = [“basketball”, “yoga”]
I did this:
filter = Enum.filter(plans, fn p -> Enum.filter(p.categories, fn c ->
c.name in selected_categories end)
end)
and
filter = Enum.filter(plans, fn p -> for category <- p.categories do
category.name in selected_categories end
end)
But for both cases, I got the same list of plans instead of the plans “123 Plan” && “All in” .
Enum.filter(plans, fn p ->
cats = Enum.map(p.categories, &(&1.name))
Enum.any?(cats, &(&1 in selected_categories))
)
But if selected_categories is going to be more than one or two items, you should probably make it a MapSet instead of a list so you can check for membership without traversing the whole list.
If possible, making the categories from plans a MapSet, would allow to simply check the size of the intersection of the two sets, leading to simpler code.
selected_categories = MapSet.new(selected_categories)
Enum.filter(plans, fn p ->
cats = MapSet.new(p.categories, &(&1.name))
i = MapSet.intersection(cats, selected_categories)
MapSet.size(i) > 0
)
Certainly possible, but this doesn’t seem any simpler than @tomkonidas’ solution, and it still adds the additional list traversal that my solution had - with MapSet.new instead of Enum.map.
EDIT: Maybe you could transform selected_categories to match the format of the cats, instead of the other way around - since you only have to do it once:
selected_categories = MapSet.new(selected_categories, fn c -> %{name: c} end)
Enum.filter(plans, fn p ->
i = MapSet.intersection(cats, selected_categories)
MapSet.size(i) > 0
)