@@ -999,6 +999,93 @@ defmodule Access do
999999 raise ArgumentError , "Access.slice/1 expected a list, got: #{ inspect ( data ) } "
10001000 end
10011001
1002+ @ doc """
1003+ Returns a function that accesses all values in a map or a keyword list.
1004+
1005+ The returned function is typically passed as an accessor to `Kernel.get_in/2`,
1006+ `Kernel.get_and_update_in/3`, and friends.
1007+
1008+ ## Examples
1009+
1010+ iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
1011+ iex> get_in(users, [Access.values(), :age]) |> Enum.sort()
1012+ [23, 27]
1013+ iex> update_in(users, [Access.values(), :age], fn age -> age + 1 end)
1014+ %{"john" => %{age: 28}, "meg" => %{age: 24}}
1015+ iex> put_in(users, [Access.values(), :planet], "Earth")
1016+ %{"john" => %{age: 27, planet: "Earth"}, "meg" => %{age: 23, planet: "Earth"}}
1017+
1018+ Values in keyword lists can be accessed as well:
1019+
1020+ iex> users = [john: %{age: 27}, meg: %{age: 23}]
1021+ iex> get_and_update_in(users, [Access.values(), :age], fn age -> {age, age + 1} end)
1022+ {[27, 23], [john: %{age: 28}, meg: %{age: 24}]}
1023+
1024+ By returning `:pop` from an accessor function, you can remove the accessed key and value
1025+ from the map or keyword list:
1026+
1027+ iex> require Integer
1028+ iex> numbers = [one: 1, two: 2, three: 3, four: 4]
1029+ iex> get_and_update_in(numbers, [Access.values()], fn num ->
1030+ ...> if Integer.is_even(num), do: :pop, else: {num, to_string(num)}
1031+ ...> end)
1032+ {[1, 2, 3, 4], [one: "1", three: "3"]}
1033+
1034+ An error is raised if the accessed structure is not a map nor a keyword list:
1035+
1036+ iex> get_in([1, 2, 3], [Access.values()])
1037+ ** (RuntimeError) Access.values/0 expected a map or a keyword list, got: [1, 2, 3]
1038+ """
1039+ @ doc since: "1.19.0"
1040+ @ spec values ( ) :: Access . access_fun ( data :: map ( ) | keyword ( ) , current_value :: list ( ) )
1041+ def values do
1042+ & values / 3
1043+ end
1044+
1045+ defp values ( :get , data = % { } , next ) do
1046+ Enum . map ( data , fn { _key , value } -> next . ( value ) end )
1047+ end
1048+
1049+ defp values ( :get_and_update , data = % { } , next ) do
1050+ { reverse_gets , updated_data } =
1051+ Enum . reduce ( data , { [ ] , % { } } , fn { key , value } , { gets , data_acc } ->
1052+ case next . ( value ) do
1053+ { get , update } -> { [ get | gets ] , Map . put ( data_acc , key , update ) }
1054+ :pop -> { [ value | gets ] , data_acc }
1055+ end
1056+ end )
1057+
1058+ { Enum . reverse ( reverse_gets ) , updated_data }
1059+ end
1060+
1061+ defp values ( op , data = [ ] , next ) do
1062+ values_keyword ( op , data , next )
1063+ end
1064+
1065+ defp values ( op , data = [ { key , _value } | _tail ] , next ) when is_atom ( key ) do
1066+ values_keyword ( op , data , next )
1067+ end
1068+
1069+ defp values ( _op , data , _next ) do
1070+ raise "Access.values/0 expected a map or a keyword list, got: #{ inspect ( data ) } "
1071+ end
1072+
1073+ defp values_keyword ( :get , data , next ) do
1074+ Enum . map ( data , fn { key , value } when is_atom ( key ) -> next . ( value ) end )
1075+ end
1076+
1077+ defp values_keyword ( :get_and_update , data , next ) do
1078+ { reverse_gets , reverse_updated_data } =
1079+ Enum . reduce ( data , { [ ] , [ ] } , fn { key , value } , { gets , data_acc } when is_atom ( key ) ->
1080+ case next . ( value ) do
1081+ { get , update } -> { [ get | gets ] , [ { key , update } | data_acc ] }
1082+ :pop -> { [ value | gets ] , data_acc }
1083+ end
1084+ end )
1085+
1086+ { Enum . reverse ( reverse_gets ) , Enum . reverse ( reverse_updated_data ) }
1087+ end
1088+
10021089 defp normalize_range ( % Range { first: first , last: last , step: step } , list )
10031090 when first < 0 or last < 0 do
10041091 count = length ( list )
0 commit comments