@@ -999,6 +999,93 @@ defmodule Access do
999
999
raise ArgumentError , "Access.slice/1 expected a list, got: #{ inspect ( data ) } "
1000
1000
end
1001
1001
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
+
1002
1089
defp normalize_range ( % Range { first: first , last: last , step: step } , list )
1003
1090
when first < 0 or last < 0 do
1004
1091
count = length ( list )
0 commit comments