Do you want to pick up from where you left of?
Take me there

Enum

Sekumpulan algoritma untuk melakukan enumerasi atas enumerables.

Enum

Modul Enum mencakup lebih dari 70 fungsi untuk bekerja pada enumrables. Semua koleksi yang sudah kita pelajari dalam pelajaran sebelumnya, dengan pengecualian tuple, adalah enumerables.

Pelajaran ini hanya akan mencakup sebagian dari fungsi yang ada, akan tetapi kita sebenarnya dapat memeriksa semuanya sendiri. Mari lakukan sedikit eksperimen pada 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
...

Dengan ini, jelas bahwa kita memiliki sejumlah fungsionalias yang sangat banyak, dan itu semua bukan tanpa alasan. Enumerasi adalah inti dari pemrograman fungsional, dan dikombinasikan dengan fasilitas lain Elixir, hal itu dapat sangat membantu para pengembang (developer).

Fungsi-fungsi umum

Untuk daftar lengkap fungsi (functions) silahkan kunjungi dokumentasi resmi Enum; untuk enumerasi lazy gunakan modul Stream.

all?

Ketika menggunakan all?/2, dan banyak Enum, kita menyediakan sebuah fungsi untuk diterapkan ke item koleksi kita. Dalam kasus all?/2, keseluruhan koleksi harus mengevaluasi true jika tidak false akan dikembalikan:

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?

Tidak seperti di atas, any? akan menghasilkan true jika setidaknya salah satu item menghasilkan true:

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

chunk_every

Jika anda perlu memecah collection jadi kelompok-kelompok yang lebih kecil, chunk_every/2 adalah fungsi yang dibutuhkan:

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

Ada beberapa opsi untuk chunk_every/4 tapi kita tidak akan membahas lebih dalam, lihat dokumentasi resmi dari fungsi ini untuk belajar lebih jauh.

chunk_by

Jika kita butuh mengelompokkan koleksi kita berdasarkan selain ukuran, kita dapat gunakan fungsi chunk_by/2. Fungsi ini membutuhkan enumerable dan sebuah fungsi, dan ketika hasil kembali fungsi tersebut berubah, sebuah kelompok baru dimulai untuk penciptaan selanjutnya. Pada contoh di bawah ini, tiap string dengan panjang yang sama dikelompokkan sampai kita bertemu string dengan panjang yang berbeda:

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

Terkadang membingkah (chungking) keluar sebuah koleksi tidak cukup untuk apa yang kita butuhkan. Jika hal tersebut pada kasus ini, map_every/3 dapat sangatlah berguna untuk mengenai setiap item nth, selalu mengenai yang pertama.

# Menerapkan fungsi setiap tiga item
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

Jika kita perlu melakukan iterasi atas sebuah collection tanpa menghasilkan sebuah value baru, kita gunakan each/2:

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

Catatan: Fungsi each/2 mengembalikan atom :ok.

map

Untuk menerapkan fungsi kita terhadap setiap item dan menghasilkan sebuah collection baru, lihatlah fungsi map/2:

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

min

min/1 menemukan nilai terkecil dalam koleksi:

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

min/2 juga melakukan hal yang sama, namun jika enumerable kosong, itu memperbolehkan kita menentukan fungsi untuk menghasilkan nilai terkecil.

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

max

max/1 mengembalikan nilai maksimal dalam koleksi:

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

max/2 adalah max/1 apa min/2 ke min/1:

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

filter

Fungsi filter/2 memungkinkan kita menyaring collection untuk hanya berisi elemen-elemen yang menghasilkan true saat dievaluasi menggunakan fungsi yang disediakan.

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

reduce

Dengan reduce/3 kita dapat memeras collection kita menjadi sebuah nilai tunggal. Untuk melakukan ini kita memasukkan sebuah akumulator yang opsional (10 dalam contoh ini) untuk dimasukkan ke dalam fungsi kita; jika tidak ada akumulator diberikan maka value pertama yang digunakan:

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

Mengurutkan collection kita menjadi mudah dengan adanya tidak hanya satu, melainkan dua, fungsi pengurutan.

sort/1 menggunakan istilah Erlang term ordering untuk menentukan urutan yang diurutkan:

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"]

Sedangkan sort/2 mengijinkan kita untuk menyediakan sebuah fungsi pengurutan kita sendiri:

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

# tanpa fungsi kita
iex> Enum.sort([%{:count => 4}, %{:count => 1}])
[%{count: 1}, %{count: 4}]

Untuk kemudahan, sort/2 membolehkan kita untuk memakai :asc atau :desc sebagai fungsi pengurutan:

Enum.sort([2, 3, 1], :desc)
[3, 2, 1]

uniq

Kita dapat menggunakan uniq_by/2 untuk membuang duplikasi dari enumerables kita:

iex> Enum.uniq([1, 2, 3, 2, 1, 1, 1, 1, 1])
[1, 2, 3]

uniq_by

uniq_by/2 juga membuang duplikasi dari enumerables, tapi memungkinkan kita untuk menyediakan fungsi yang mengecek keunikan.

iex> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 3}], fn coord -> coord.y end)
[%{x: 1, y: 1}, %{x: 3, y: 3}]

Enum menggunakan operator Capture (&)

Banyak fungsi-fungsi di dalam modul Enum di Elixir menerima fungsi anonim sebagai argumen untuk digunakan pada tiap iterable dari enumerables yang dikirim.

Fungsi anonim tersebut biasanya ditulis secara singkat menggunakan operator Capture (&).

Berikut adalah beberapa contoh untuk menunjukkan bagaimana operator Capture dapat diimplementasikan dengan modul Enum. Tiap versi memiliki fungsionalitas yang setara.

Menggunakan operator capture dengan fungsi anonim

Di bawah ini adalah contoh tipikal dari sintaks standar ketika mengirim sebuah fungsi anonim ke Enum.map/2.

iex> Enum.map([1,2,3], fn number -> number + 3 end)
[4, 5, 6]

Sekarang kita pakai operator capture (&); menangkap tiap iterasi dari sebuah daftar angka ([1,2,3]) dan memasukkan tiap iterasi ke variabel &1 saat dikirim ke fungsi pemetaan.

iex> Enum.map([1,2,3], &(&1 + 3))
[4, 5, 6]

Cara ini dapat di-refactor lebih lanjut dengan memasukkan fungsi anonim dengan operator capture di atas ke dalam sebuah variabel dan menggunakannya di fungsi Enum.map/2.

iex> plus_three = &(&1 + 3)
iex> Enum.map([1,2,3], plus_three)
[4, 5, 6]

Menggunakan operator capture dengan named function

Pertama kita buat named function dan memanggilnya di dalam fungsi anonim yang didefinisikan di Enum.map/2.

defmodule Adding do
def plus_three(number), do: number + 3
end

iex>  Enum.map([1,2,3], fn number -> Adding.plus_three(number) end)
[4, 5, 6]

Kemudian kita bisa refactor menggunakan operator capture.

iex> Enum.map([1,2,3], &Adding.plus_three(&1))
[4, 5, 6]

Untuk sintaks paling ringkas, kita dapat memanggil named function tanpa menuliskan variabel capture (&1) secara eksplisit.

iex> Enum.map([1,2,3], &Adding.plus_three/1)
[4, 5, 6]
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!