Why Enum.filter_map is deprecated?

I just upgrade OTP to 20.0 and elixir to 1.5.0-rc.1, I got warnings on usage of filter_map. Why is it deprecated?

2 Likes

Changelog for 1.5 says:

[Enum] Deprecate Enum.filter_map/3 in favor of Enum.filter/2 + Enum.map/2 or for-comprehensions

1 Like

Yes, ‘filter+map’ or ‘for’ works, but I want to know the reason behind it.

I think the reason was the fact that it was an outlier. The only “fused” function.

2 Likes

Enum.map_join = Enum.map + Enum.join

Enum.flat_map = Enum.map + Enum.concat

Can someone help a rookie (me) understand the

[
] or for comprehensions

component of this deprecation message?

Parsing the English can be odd, but they’re these things, covered in the Guide https://elixir-lang.org/getting-started/comprehensions.html

for comprehensions consist of generators and filters, though the filters seem to be less widely known

2 Likes

Here is an example of using comprehension in lieu of Enum.filter_map/3:

# filter even numbers
filter_fn = fn n ->
  rem(n, 2) == 0
end

# double
map_fn = fn n ->
  n * 2
end

some_enum = 1..20

# filter_map version
Enum.filter_map(some_enum, filter_fn, map_fn)

# comprehension version
for n <- some_enum, filter_fn.(n), do: map_fn.(n)
3 Likes

What about flat_map, flat_map_reduce, map_reduce, map_join and the rest of the “fused” function?

Most of them are useful as a way to avoid walking a long list twice.

1 Like

If you fear multi walks, then use the appropriate Stream functions.

So the flat_map, flat_map_reduce, map_reduce and map_join style Enum functions is just a legacy implementation? They don’t give us anything extra, they just help us avoid some extra code for some common use-cases?

1 Like

Technically flat_map, like reduce are considered base-most enumeration functions. flat_map is the base-most that always returns another enumeration, and reduce is the base-most that returns anything. filter_map is pretty duplicative as I always use flat_map anyways.

3 Likes

thanks!

[1, 2, 3]
|> Enum.map(&(&1+3))
|> Enum.reject(&(&1==4))
# => [5, 6]

=>

[1, 2, 3]
|> Enum.flat_map(fn n ->
  case n+3 do
    4 -> []
    n -> [n]
  end
end)
# => [5, 6]

Hello from the future. The commit log doesn’t mention why it was deprecated, but whatyouhide, a core team member, explained a bit on this issue:

Elixir deprecated the usage of Enum.filter_map/3 because it was counterintuitive and the same could be achieved with Enum.filter/2 + Enum.map/2, with Stream, or with a for comprehension.

And also José Valim in the mailing list:

I would use comprehensions:

for item <- 1, 2, 3,
    rem(item, 2) == 0,
    do: item * 2

There is no reason for Enum.filter_map or Enum.map_filter to exist besides backwards compatibility.

5 Likes