siddhant3030

siddhant3030

How can we write our own filter function?

Suppose there is a list of numbers and I want to filter the list.

sample input

3
10
9
8
2
7
5
1
3
0

output

2
1
0

I want to take this input and filter out the list which is less than 3

Most Liked

chouzar

chouzar

By using recursion and taking advantage of the cons operator we can implement the function by ourselves.

First, the cons operator lets us pattern match on lists:

# Our head equals 3 and our tail to the rest of the list
iex> [head | tail] = [1, 2, 3]
[1,2,3]
iex> head
1
iex> tail
[2,3]

By using recursion we should be able to traverse a list:

defmodule Recurr do

  def traverse(list) do
    [head|tail] = list
    IO.puts(head)   
    traverse(tail)
  end

end

The function above would print numbers just fine:

iex> Recurr.traverse [1,2,3]
1
2
3
** (MatchError) no match of right hand side value: []
    iex:8: Recurr.traverse/1

But fails at the end, this is because we have an empty list and we’re trying to match:

defmodule Recurr do

  def traverse(list) do
    [head|tail] = list
    # Changed printing, just to be more instructive
    IO.puts("[ #{inspect(head)} | #{inspect(tail)} ] = #{inspect(list)} ")
    traverse(tail)
  end

end

iex> Recurr.traverse [1,2,3]                                                
[ 1 | [2, 3] ] = [1, 2, 3] 
[ 2 | [3] ] = [2, 3] 
[ 3 | [] ] = [3] 
** (MatchError) no match of right hand side value: []

# Because we're sending an empty list [] to traverse(list) it cannot 
# further match with the cons operator

We just need to state our base case, the function that should match when our list is empty:

defmodule Recurr do

  def traverse([]) do
    IO.puts "end"
  end

  def traverse(list) do
    [head|tail] = list
    IO.puts("[ #{inspect(head)} | #{inspect(tail)} ] = #{inspect(list)} ")
    traverse(tail)
  end

end

iex> Recurr.traverse [1,2,3]                                                
[ 1 | [2, 3] ] = [1, 2, 3] 
[ 2 | [3] ] = [2, 3] 
[ 3 | [] ] = [3] 
end
:ok

Now… filtering is a matter of following this recursion pattern. You could try something very similar that filters say “pair numbers”.

defmodule Recurr do

  def filter_pairs([]) do
    
  end

  def filter_pairs([head | tail] = list) do
    # filter logic
    filter_pairs(list) # recursion
  end

end

After that you could make it more generic by passing/injecting a function into a more generic filter(list, fun) function :slight_smile:.

I hope I didn’t overexplained too much, any doubts just let me know.

In this case the input was a linked list, therefore the cons [ | ] = list operator is a natural fit. Other data structures would require a different approach.

ericgray

ericgray

As others above have mentioned using recursion will get the job done. You can define a function filter_numbers(list, fun) that takes a list and a function and returns a new list of filtered numbers. Using recursion you can write it like this.

defmodule MyFilter do

  def filter_numbers(list, fun) do
    filter_numbers(list, fun, [])
  end

  def filter_numbers([], _fun, acc) do
    Enum.reverse(acc)
  end

  def filter_numbers([h|t], fun, acc) do
    case fun.(h) do
      true ->
        filter_numbers(t, fun, [h|acc])

      false ->
        filter_numbers(t, fun, acc)
    end
  end

end
iex> numbers = [3, 10, 9, 8, 2, 7, 5, 1, 3, 0]
[3, 10, 9, 8, 2, 7, 5, 1, 3, 0]

iex> MyFilter.filter_numbers(numbers, & &1 < 3)
[2, 1, 0]

iex> MyFilter.filter_numbers(numbers, & &1 > 3)
[10, 9, 8, 7, 5]
mudasobwa

mudasobwa

Creator of Cure

In a nutshell:

input = [3, 10, 2, 7, 1]
Enum.reject(input, fn e -> e >= 3 end)
#⇒ [2, 1]
Enum.filter(input, & &1 < 3)
#⇒ [2, 1]
mindok

mindok

The examples are in Erlang, but I do like the explanations of recursion/tail recursion/why reverse is needed etc here: https://learnyousomeerlang.com/recursion#hello-recursion

Where Next?

Popular in Questions Top

openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
makeitrein
Hey all, just started picking up Elixir last week and am writing a scraper as a learning project. Baby step #1 is extracting the number ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New

Other popular topics Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

We're in Beta

About us Mission Statement