Enum

Cette traduction est à jour.

Un ensemble d’algorithmes pour énumérer sur les collections.

Table des matières

Enum

Le module Enum inclus plus de 70 fonctions pour travailler sur les énumérables. Toutes les collections que nous avons vues dans la leçon précédente, à l’exception des Tuples, sont énumérables.

Cette leçon ne couvre qu’une partie des fonctions disponibles, cependant nous pouvons les examiner par nous même. Faisons une petite expérience dans IEx.

iex> Enum.__info__(:functions) |> Enum.each(fn({function, arity}) ->
...>   IO.puts "#{function}/#{arity}"
...> end)
all?/1
all?/2
any?/1
any?/2
at/2
at/3
...

En utilisant ceci, il devient clair que nous avons accès à un grand nombre de fonctionalités, et ceci pour une très bonne raison. L’énumeration est au coeur de la programmation fonctionelle et est incroyablement utile. En l’utilisant en combinaison avec d’autres avantages Elixir, comme par exemple la documentation traitée en tant que citoyen de première classe, comme nous venons de le voir, cela peut être une grande source de puissance pour le dévelopeur.

Pour la liste complète des fonctions voir la documentation officielle du module Enum; pour l’énumération paresseuse utilisez le module Stream.

all?

Lorsque nous utilisons all?/2, et la plupart de fonctions du module Enum, nous fournissons une fonction à appliquer aux éléments de nos collections. Dans le cas de all?/2, toute la collection dois être évaluée à true sinon false sera retourné:

iex> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 3 end)
false
iex> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) > 1 end)
true

any?

Contrairement à ci-dessus, any?/2 retournera true si au moins un élément est évalué à true:

iex> Enum.any?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 5 end)
true

chunk_every/2

Si vous devez diviser vos collections en plus petits groupes d’une taille donnée, chunk_every/2 est la fonction que vous recherchez:

iex> Enum.chunk_every([1, 2, 3, 4, 5, 6], 2)
[[1, 2], [3, 4], [5, 6]]

Il existe quelques options pour chunk_every/4 que nous ne verrons pas, mais que vous pouvez consulter dans la documentation officielle de chunk_every/4 pour en savoir plus.

chunk_by

Si on veut grouper nos collections autrement que par taille, on peux utiliser la fonction chunk_by/2. Elle prend un énumérable donné et une fonction, et quand le retour de cette fonction change, un nouveau groupe est crée:

iex> Enum.chunk_by(["one", "two", "three", "four", "five"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"]]
iex> Enum.chunk_by(["one", "two", "three", "four", "five", "six"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"], ["six"]]

map_every

Parfois, diviser une collection en petits groupes n’est pas exactement ce dont on a besoin. Si c’est le cas, map_every/3 peut être très utile pour agir sur chaque nième élément, en commencant toujours par le premier:

# Apply function every three items
iex> Enum.map_every([1, 2, 3, 4, 5, 6, 7, 8], 3, fn x -> x + 1000 end)
[1001, 2, 3, 1004, 5, 6, 1007, 8]

each

Pour itérer sur une collection sans produire une nouvelle valeur, on utilise each/2:

iex> Enum.each(["one", "two", "three"], fn(s) -> IO.puts(s) end)
one
two
three
:ok

Note: La fonction each/2 retourne l’atom :ok.

map

Pour appliquer une fonction à chaque élément et produire une nouvelle collection, nous avons la fonction map/2:

iex> Enum.map([0, 1, 2, 3], fn(x) -> x - 1 end)
[-1, 0, 1, 2]

min

min/1 trouve la valeur minimale d’une collection:

iex> Enum.min([5, 3, 0, -1])
-1

min/2 fait la même chose, mais si l’énumérable est vide, nous permet de spécifier une fonction pour produire la valeur minimum.

iex> Enum.min([], fn -> :foo end)
:foo

max

max/1 retourne la valeur maximale d’une collection:

iex> Enum.max([5, 3, 0, -1])
5

max/2 est à max/1 ce que min/2 est à min/1:

Enum.max([], fn -> :bar end)
:bar

filter

La fonction filter/2 nous permet de filtrer une collection en ne retournant que les éléments qui permettent à la fonction fournie de retourner une évaluation true.

iex> Enum.filter([1, 2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
[2, 4]

reduce

Avec reduce/3 nous pouvons réduire nos collections à une unique valeur. Pour cela nous passons un accumulateur optionnel (10 dans cet exemple) à notre fonction; s’il n’y a pas d’accumulateur, la première valeur est utilisée:

iex> Enum.reduce([1, 2, 3], 10, fn(x, acc) -> x + acc end)
16
iex> Enum.reduce([1, 2, 3], fn(x, acc) -> x + acc end)
6
iex> Enum.reduce(["a","b","c"], "1", fn(x,acc)-> x <> acc end)
"cba1"

sort

Le tri de collections est facilité avec non pas une, mais deux fonctions de tri.

sort/1 utilise le tri de termes d’Erlang pour déterminer l’ordre:

iex> Enum.sort([5, 6, 1, 3, -1, 4])
[-1, 1, 3, 4, 5, 6]

iex> Enum.sort([:foo, "bar", Enum, -1, 4])
[-1, 4, Enum, :foo, "bar"]

sort/2 nous permet de fournir notre propre fonction de tri:

# avec notre fonction
iex> Enum.sort([%{:val => 4}, %{:val => 1}], fn(x, y) -> x[:val] > y[:val] end)
[%{val: 4}, %{val: 1}]

# sans notre fonction
iex> Enum.sort([%{:count => 4}, %{:count => 1}])
[%{count: 1}, %{count: 4}]

uniq_by

uniq_by/2 va supprimer les doublons de nos collections:

iex> Enum.uniq_by([1, 2, 3, 2, 1, 1, 1, 1, 1], fn x -> x end)
[1, 2, 3]