Skip to content

Commit 6b5da55

Browse files
committed
Elixir library: add support to Enumerable to Enum.find_index/find_value
Code has been taken from here: https://raw.githubusercontent.com/elixir-lang/elixir/v1.10.4/lib/elixir/lib/enum.ex Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent 539ffd5 commit 6b5da55

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ also non string parameters (e.g. `Enum.join([1, 2], ",")`
2323

2424
- ESP32: Elixir library is not shipped anymore with `esp32boot.avm`. Use `elixir_esp32boot.avm`
2525
instead
26+
- `Enum.find_index` and `Enum.find_value` support Enumerable and not just lists
2627

2728
### Fixed
2829

libs/exavmlib/lib/Enum.ex

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,14 +398,67 @@ defmodule Enum do
398398
|> elem(1)
399399
end
400400

401+
@doc """
402+
Similar to `find/3`, but returns the index (zero-based)
403+
of the element instead of the element itself.
404+
405+
## Examples
406+
407+
iex> Enum.find_index([2, 4, 6], fn x -> rem(x, 2) == 1 end)
408+
nil
409+
410+
iex> Enum.find_index([2, 3, 4], fn x -> rem(x, 2) == 1 end)
411+
1
412+
413+
"""
414+
@spec find_index(t, (element -> any)) :: non_neg_integer | nil
401415
def find_index(enumerable, fun) when is_list(enumerable) do
402416
find_index_list(enumerable, 0, fun)
403417
end
404418

419+
def find_index(enumerable, fun) do
420+
result =
421+
Enumerable.reduce(enumerable, {:cont, {:not_found, 0}}, fn entry, {_, index} ->
422+
if fun.(entry), do: {:halt, {:found, index}}, else: {:cont, {:not_found, index + 1}}
423+
end)
424+
425+
case elem(result, 1) do
426+
{:found, index} -> index
427+
{:not_found, _} -> nil
428+
end
429+
end
430+
431+
@doc """
432+
Similar to `find/3`, but returns the value of the function
433+
invocation instead of the element itself.
434+
435+
## Examples
436+
437+
iex> Enum.find_value([2, 4, 6], fn x -> rem(x, 2) == 1 end)
438+
nil
439+
440+
iex> Enum.find_value([2, 3, 4], fn x -> rem(x, 2) == 1 end)
441+
true
442+
443+
iex> Enum.find_value([1, 2, 3], "no bools!", &is_boolean/1)
444+
"no bools!"
445+
446+
"""
447+
@spec find_value(t, any, (element -> any)) :: any | nil
448+
def find_value(enumerable, default \\ nil, fun)
449+
405450
def find_value(enumerable, default, fun) when is_list(enumerable) do
406451
find_value_list(enumerable, default, fun)
407452
end
408453

454+
def find_value(enumerable, default, fun) do
455+
Enumerable.reduce(enumerable, {:cont, default}, fn entry, default ->
456+
fun_entry = fun.(entry)
457+
if fun_entry, do: {:halt, fun_entry}, else: {:cont, default}
458+
end)
459+
|> elem(1)
460+
end
461+
409462
@doc """
410463
Maps the given `fun` over `enumerable` and flattens the result.
411464

tests/libs/exavmlib/Tests.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ defmodule Tests do
3939
[2, 3] = Enum.slice([1, 2, 3], 1, 2)
4040
:test = Enum.at([0, 1, :test, 3], 2)
4141
:atom = Enum.find([1, 2, :atom, 3, 4], -1, fn item -> not is_integer(item) end)
42+
1 = Enum.find_index([:a, :b, :c], fn item -> item == :b end)
43+
true = Enum.find_value([-2, -3, -1, 0, 1], fn item -> item >= 0 end)
4244
true = Enum.all?([1, 2, 3], fn n -> n >= 0 end)
4345
true = Enum.any?([1, -2, 3], fn n -> n < 0 end)
4446
[2] = Enum.filter([1, 2, 3], fn n -> rem(n, 2) == 0 end)
@@ -64,6 +66,7 @@ defmodule Tests do
6466
true = at_0 != at_1
6567
{:c, :atom} = Enum.find(%{a: 1, b: 2, c: :atom, d: 3}, fn {_k, v} -> not is_integer(v) end)
6668
{:d, 3} = Enum.find(%{a: 1, b: 2, c: :atom, d: 3}, fn {k, _v} -> k == :d end)
69+
true = Enum.find_value(%{"a" => 1, b: 2}, fn {k, _v} -> is_atom(k) end)
6770
true = Enum.all?(%{a: 1, b: 2}, fn {_k, v} -> v >= 0 end)
6871
true = Enum.any?(%{a: 1, b: -2}, fn {_k, v} -> v < 0 end)
6972
[b: 2] = Enum.filter(%{a: 1, b: 2, c: 3}, fn {_k, v} -> rem(v, 2) == 0 end)
@@ -82,6 +85,7 @@ defmodule Tests do
8285
true = ms_at_0 == 1 or ms_at_0 == 2
8386
true = ms_at_1 == 1 or ms_at_1 == 2
8487
:atom = Enum.find(MapSet.new([1, 2, :atom, 3, 4]), fn item -> not is_integer(item) end)
88+
nil = Enum.find_value([-2, -3, -1, 0, 1], fn item -> item > 100 end)
8589
true = Enum.all?(MapSet.new([1, 2, 3]), fn n -> n >= 0 end)
8690
true = Enum.any?(MapSet.new([1, -2, 3]), fn n -> n < 0 end)
8791
[2] = Enum.filter(MapSet.new([1, 2, 3]), fn n -> rem(n, 2) == 0 end)
@@ -97,6 +101,7 @@ defmodule Tests do
97101
[6, 7, 8, 9, 10] = Enum.slice(1..10, 5, 100)
98102
7 = Enum.at(1..10, 6)
99103
8 = Enum.find(-10..10, fn item -> item >= 8 end)
104+
true = Enum.find_value(-10..10, fn item -> item >= 0 end)
100105
true = Enum.all?(0..10, fn n -> n >= 0 end)
101106
true = Enum.any?(-1..10, fn n -> n < 0 end)
102107
[0, 1, 2] = Enum.filter(-10..2, fn n -> n >= 0 end)

0 commit comments

Comments
 (0)