Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find indexes from list in Elixir

With Enum.find_index/2, we can find the index of an element. However, if a same element occurs several times, how can we do?

I would like to have this behavior:

iex> find_indexes(["a", "b", "c", "b", "b"], fn(x) -> x == "a" end)
[0]

iex> find_indexes(["a", "b", "c", "b", "b"], fn(x) -> x == "c" end)
[2]

iex> find_indexes(["a", "b", "c", "b", "b"], fn(x) -> x == "b" end)
[1, 3, 4]

Thanks for any ideas.

like image 722
T5i Avatar asked Aug 31 '13 19:08

T5i


2 Answers

I couldn't find a exact function in library, so tried to implement it. Hope it can help some.

defmodule Sample1 do
  # combining Enum functions
  def find_indexes(collection, function) do
    Enum.filter_map(Enum.with_index(collection), fn({x, _y}) -> function.(x) end, elem(&1, 1))
  end
end

defmodule Sample2 do
  # implementing as similar way as Enum.find_index
  def find_indexes(collection, function) do
    do_find_indexes(collection, function, 0, [])
  end

  def do_find_indexes([], _function, _counter, acc) do
    Enum.reverse(acc)
  end

  def do_find_indexes([h|t], function, counter, acc) do
    if function.(h) do
      do_find_indexes(t, function, counter + 1, [counter|acc])
    else
      do_find_indexes(t, function, counter + 1, acc)
    end
  end
end

IO.puts "Sample1"
IO.inspect Sample1.find_indexes(["a", "b", "c", "b", "b"], fn(x) -> x == "a" end)
IO.inspect Sample1.find_indexes(["a", "b", "c", "b", "b"], fn(x) -> x == "c" end)
IO.inspect Sample1.find_indexes(["a", "b", "c", "b", "b"], fn(x) -> x == "b" end)

IO.puts "Sample2"
IO.inspect Sample2.find_indexes(["a", "b", "c", "b", "b"], fn(x) -> x == "a" end)
IO.inspect Sample2.find_indexes(["a", "b", "c", "b", "b"], fn(x) -> x == "c" end)
IO.inspect Sample2.find_indexes(["a", "b", "c", "b", "b"], fn(x) -> x == "b" end)

Executed as follows,

% elixir find.ex
Sample1
[0]
[2]
[1, 3, 4]
Sample2
[0]
[2]
[1, 3, 4]
like image 160
parroty Avatar answered Sep 23 '22 18:09

parroty


Alternatively, you can zip the list with a range of 0..length(list) and filter the list using the new item:

line = IO.read(:stdio, :all) 
|> String.split
|> Enum.zip(0..100)
|> Enum.filter(fn({_, x}) -> rem(x, 2) != 0 end)
|> Enum.map(fn({x, _}) -> "#{x}\n" end)

Which filter odd elements on the given list from stdin.

Please note that 100 in the range (0..100) must be the length of your list. I assumed I have a list of 100 items.

like image 33
Afshin Mehrabani Avatar answered Sep 23 '22 18:09

Afshin Mehrabani