How to filter a list of tuples by the first element of each tuple?

I have a list of two-element tuples {name, number} and would like to only output those tuples whose first elements (name) matches a name or is in a list of names I provide it. For example:

list_of_structs_with_tons_of_user_info
|> Enum.map(&{&1.name, &1.number})
|> # filter this list of tuples according to the names I want to display

Any help is appreciated. Thanks!

Hi @ConstantBall what have you tried so far?

1 Like

Like so?:

list_of_structs_with_tons_of_user_info
|> Enum.map(&{&1.name, &1.number})
|> Enum.filter(fn {name, number} -> name in ["A", "B", "C"] end)

Hi @benwilson512. So I’ve tried multiple which resulted in errors, some of which included case, cond, Enum.map with a guard clause in the anonymous function.

This is the only one which didn’t result in an exception but all tuples that didn’t satisfies the conditions were output as nil (which is expected, but there is way too many and I only want )

list_of_structs_with_tons_of_user_info
|> Enum.map(&{&1.name, &1.number}) 
|> Enum.map(fn
{name, num} when name == "Luke" -> {name, num}
{name, num} when name == "Mike" -> {name, num}
{name, num} when name == "Karl" -> {name, num}
_ -> nil
end
)

Currently stuck on figuring out a better solution.

I had tried Enum.filter but I’m now noticing that I did it incorrectly. Thanks for showing me what I did wrong!
By the way, how would you write the & shorthand for that function? I originally thought Enum.filter(&{&1 in ["A", "B", "C"], &2) but that resulted in an arity error, and Enum.filter(&(&1 in ["A", "B", "C"])) only gives the names.

If my reading is accurate today, Enum.filter(&(elem(&1, 0) in ["A", "B", "C"])) should be closer, using Kernel.elem/2 to peek at the first term in the tuple.

It worked. Thank you so much!

When I see mapping and filtering together, I always think of my fondness of for comprehensions. There’s no need for the intermediate tuple form. This will pattern match the struct, then filter, then output in the desired 2-tuple format.

desired_names = ["A", "B"]
for %{name: n, number: num} <- list_of_structs_with_tons_of_user_info, n in desired_names, do: {n, num}
6 Likes

When you need to map and filter I like to use flat_map:

desired_names = ["A", "B"]

list_of_structs_with_tons_of_user_info
|> Enum.flat_map(fn %{name: name, number: number} ->
  if name in desired_names do
    [{name, number}]
  else
    []
  end
end)

Although for comprehensions are usually shorter.

3 Likes