@@ -422,4 +422,70 @@ defmodule Access do
422
422
defp all ( [ ] , _next , gets , updates ) do
423
423
{ :lists . reverse ( gets ) , :lists . reverse ( updates ) }
424
424
end
425
+
426
+ @ doc ~S"""
427
+ Accesses the element at `index` (zero based) of a list.
428
+
429
+ ## Examples
430
+
431
+ iex> list = [%{name: "john"}, %{name: "mary"}]
432
+ iex> get_in(list, [Access.at(1), :name])
433
+ "mary"
434
+ iex> get_and_update_in(list, [Access.at(0), :name], fn
435
+ ...> prev -> {prev, String.upcase(prev)}
436
+ ...> end)
437
+ {"john", [%{name: "JOHN"}, %{name: "mary"}]}
438
+ iex> pop_in(list, [Access.at(0), :name])
439
+ {"john", [%{}, %{name: "mary"}]}
440
+
441
+ When the index is out of bounds, `nil` is returned and the update function is never called:
442
+
443
+ iex> list = [%{name: "john"}, %{name: "mary"}]
444
+ iex> get_in(list, [Access.at(10), :name])
445
+ nil
446
+ iex> get_and_update_in(list, [Access.at(10), :name], fn
447
+ ...> prev -> {prev, String.upcase(prev)}
448
+ ...> end)
449
+ {nil, [%{name: "john"}, %{name: "mary"}]}
450
+
451
+ An error is raised for negative indexes:
452
+
453
+ iex> get_in([], [Access.at(-1)])
454
+ ** (FunctionClauseError) no function clause matching in Access.at/1
455
+
456
+ An error is raised if the accessed structure is not a list:
457
+
458
+ iex> get_in(%{}, [Access.at(1)])
459
+ ** (RuntimeError) Access.at/1 expected a list, got: %{}
460
+ """
461
+ def at ( index ) when index >= 0 do
462
+ fn ( op , data , next ) -> at ( op , data , index , next ) end
463
+ end
464
+
465
+ defp at ( :get , data , index , next ) when is_list ( data ) do
466
+ data |> Enum . at ( index ) |> next . ( )
467
+ end
468
+
469
+ defp at ( :get_and_update , data , index , next ) when is_list ( data ) do
470
+ get_and_update_at ( data , index , next , [ ] )
471
+ end
472
+
473
+ defp at ( _op , data , _index , _next ) do
474
+ raise "Access.at/1 expected a list, got: #{ inspect data } "
475
+ end
476
+
477
+ defp get_and_update_at ( [ head | rest ] , 0 , next , updates ) do
478
+ case next . ( head ) do
479
+ { get , update } -> { get , :lists . reverse ( [ update | updates ] , rest ) }
480
+ :pop -> { head , :lists . reverse ( [ head | updates ] , rest ) }
481
+ end
482
+ end
483
+
484
+ defp get_and_update_at ( [ head | rest ] , index , next , updates ) do
485
+ get_and_update_at ( rest , index - 1 , next , [ head | updates ] )
486
+ end
487
+
488
+ defp get_and_update_at ( [ ] , _index , _next , updates ) do
489
+ { nil , :lists . reverse ( updates ) }
490
+ end
425
491
end
0 commit comments