@@ -24,6 +24,7 @@ defmodule Enum do
24
24
@ compile { :autoload , false }
25
25
26
26
@ type t :: Enumerable . t ( )
27
+ @ type acc :: any
27
28
@ type index :: integer
28
29
@ type element :: any
29
30
@@ -209,6 +210,98 @@ defmodule Enum do
209
210
end
210
211
end
211
212
213
+ @ doc """
214
+ Chunks the `enumerable` with fine grained control when every chunk is emitted.
215
+
216
+ `chunk_fun` receives the current element and the accumulator and
217
+ must return `{:cont, chunk, acc}` to emit the given chunk and
218
+ continue with accumulator or `{:cont, acc}` to not emit any chunk
219
+ and continue with the return accumulator.
220
+
221
+ `after_fun` is invoked when iteration is done and must also return
222
+ `{:cont, chunk, acc}` or `{:cont, acc}`.
223
+
224
+ Returns a list of lists.
225
+
226
+ ## Examples
227
+
228
+ iex> chunk_fun = fn element, acc ->
229
+ ...> if rem(element, 2) == 0 do
230
+ ...> {:cont, Enum.reverse([element | acc]), []}
231
+ ...> else
232
+ ...> {:cont, [element | acc]}
233
+ ...> end
234
+ ...> end
235
+ iex> after_fun = fn
236
+ ...> [] -> {:cont, []}
237
+ ...> acc -> {:cont, Enum.reverse(acc), []}
238
+ ...> end
239
+ iex> Enum.chunk_while(1..10, [], chunk_fun, after_fun)
240
+ [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
241
+
242
+ """
243
+ @ doc since: "1.5.0"
244
+ @ spec chunk_while (
245
+ t ,
246
+ acc ,
247
+ ( element , acc -> { :cont , chunk , acc } | { :cont , acc } | { :halt , acc } ) ,
248
+ ( acc -> { :cont , chunk , acc } | { :cont , acc } )
249
+ ) :: Enumerable . t ( )
250
+ when chunk: any
251
+ def chunk_while ( enumerable , acc , chunk_fun , after_fun ) do
252
+ { _ , { res , acc } } =
253
+ Enumerable . reduce ( enumerable , { :cont , { [ ] , acc } } , fn entry , { buffer , acc } ->
254
+ case chunk_fun . ( entry , acc ) do
255
+ { :cont , emit , acc } -> { :cont , { [ emit | buffer ] , acc } }
256
+ { :cont , acc } -> { :cont , { buffer , acc } }
257
+ { :halt , acc } -> { :halt , { buffer , acc } }
258
+ end
259
+ end )
260
+
261
+ case after_fun . ( acc ) do
262
+ { :cont , _acc } -> :lists . reverse ( res )
263
+ { :cont , elem , _acc } -> :lists . reverse ( [ elem | res ] )
264
+ end
265
+ end
266
+
267
+ @ doc """
268
+ Splits enumerable on every element for which `fun` returns a new
269
+ value.
270
+
271
+ Returns a list of lists.
272
+
273
+ ## Examples
274
+
275
+ iex> Enum.chunk_by([1, 2, 2, 3, 4, 4, 6, 7, 7], &(rem(&1, 2) == 1))
276
+ [[1], [2, 2], [3], [4, 4, 6], [7, 7]]
277
+
278
+ """
279
+ @ spec chunk_by ( t , ( element -> any ) ) :: [ list ]
280
+ def chunk_by ( enumerable , fun ) do
281
+ reducers_chunk_by ( & chunk_while / 4 , enumerable , fun )
282
+ end
283
+
284
+ # Taken from Stream.Reducers
285
+ defp reducers_chunk_by ( chunk_by , enumerable , fun ) do
286
+ chunk_fun = fn
287
+ entry , nil ->
288
+ { :cont , { [ entry ] , fun . ( entry ) } }
289
+
290
+ entry , { acc , value } ->
291
+ case fun . ( entry ) do
292
+ ^ value -> { :cont , { [ entry | acc ] , value } }
293
+ new_value -> { :cont , :lists . reverse ( acc ) , { [ entry ] , new_value } }
294
+ end
295
+ end
296
+
297
+ after_fun = fn
298
+ nil -> { :cont , :done }
299
+ { acc , _value } -> { :cont , :lists . reverse ( acc ) , :done }
300
+ end
301
+
302
+ chunk_by . ( enumerable , nil , chunk_fun , after_fun )
303
+ end
304
+
212
305
@ doc """
213
306
Invokes the given `fun` for each element in the `enumerable`.
214
307
@@ -305,14 +398,101 @@ defmodule Enum do
305
398
|> elem ( 1 )
306
399
end
307
400
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
308
415
def find_index ( enumerable , fun ) when is_list ( enumerable ) do
309
416
find_index_list ( enumerable , 0 , fun )
310
417
end
311
418
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
+
312
450
def find_value ( enumerable , default , fun ) when is_list ( enumerable ) do
313
451
find_value_list ( enumerable , default , fun )
314
452
end
315
453
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
+
462
+ @ doc """
463
+ Maps the given `fun` over `enumerable` and flattens the result.
464
+
465
+ This function returns a new enumerable built by appending the result of invoking `fun`
466
+ on each element of `enumerable` together; conceptually, this is similar to a
467
+ combination of `map/2` and `concat/1`.
468
+
469
+ ## Examples
470
+
471
+ iex> Enum.flat_map([:a, :b, :c], fn x -> [x, x] end)
472
+ [:a, :a, :b, :b, :c, :c]
473
+
474
+ iex> Enum.flat_map([{1, 3}, {4, 6}], fn {x, y} -> x..y end)
475
+ [1, 2, 3, 4, 5, 6]
476
+
477
+ iex> Enum.flat_map([:a, :b, :c], fn x -> [[x]] end)
478
+ [[:a], [:b], [:c]]
479
+
480
+ """
481
+ @ spec flat_map ( t , ( element -> t ) ) :: list
482
+ def flat_map ( enumerable , fun ) when is_list ( enumerable ) do
483
+ flat_map_list ( enumerable , fun )
484
+ end
485
+
486
+ def flat_map ( enumerable , fun ) do
487
+ reduce ( enumerable , [ ] , fn entry , acc ->
488
+ case fun . ( entry ) do
489
+ list when is_list ( list ) -> :lists . reverse ( list , acc )
490
+ other -> reduce ( other , acc , & [ & 1 | & 2 ] )
491
+ end
492
+ end )
493
+ |> :lists . reverse ( )
494
+ end
495
+
316
496
@ doc """
317
497
Returns a list where each element is the result of invoking
318
498
`fun` on each corresponding element of `enumerable`.
@@ -415,10 +595,6 @@ defmodule Enum do
415
595
end
416
596
end
417
597
418
- def reject ( enumerable , fun ) when is_list ( enumerable ) do
419
- reject_list ( enumerable , fun )
420
- end
421
-
422
598
## all?
423
599
424
600
defp all_list ( [ h | t ] , fun ) do
@@ -499,6 +675,19 @@ defmodule Enum do
499
675
default
500
676
end
501
677
678
+ ## flat_map
679
+
680
+ defp flat_map_list ( [ head | tail ] , fun ) do
681
+ case fun . ( head ) do
682
+ list when is_list ( list ) -> list ++ flat_map_list ( tail , fun )
683
+ other -> to_list ( other ) ++ flat_map_list ( tail , fun )
684
+ end
685
+ end
686
+
687
+ defp flat_map_list ( [ ] , _fun ) do
688
+ [ ]
689
+ end
690
+
502
691
@ doc """
503
692
Inserts the given `enumerable` into a `collectable`.
504
693
@@ -650,6 +839,27 @@ defmodule Enum do
650
839
[ ]
651
840
end
652
841
842
+ @ doc """
843
+ Returns a list of elements in `enumerable` excluding those for which the function `fun` returns
844
+ a truthy value.
845
+
846
+ See also `filter/2`.
847
+
848
+ ## Examples
849
+
850
+ iex> Enum.reject([1, 2, 3], fn x -> rem(x, 2) == 0 end)
851
+ [1, 3]
852
+
853
+ """
854
+ @ spec reject ( t , ( element -> as_boolean ( term ) ) ) :: list
855
+ def reject ( enumerable , fun ) when is_list ( enumerable ) do
856
+ reject_list ( enumerable , fun )
857
+ end
858
+
859
+ def reject ( enumerable , fun ) do
860
+ reduce ( enumerable , [ ] , R . reject ( fun ) ) |> :lists . reverse ( )
861
+ end
862
+
653
863
@ doc """
654
864
Returns a list of elements in `enumerable` in reverse order.
655
865
0 commit comments