@@ -999,6 +999,124 @@ 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.
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+ See `keys/0` for a function that accesses all keys in a map.
1009+
1010+ ## Examples
1011+
1012+ iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
1013+ iex> get_in(users, [Access.values(), :age])
1014+ [27, 23]
1015+ iex> update_in(users, [Access.values(), :age], fn age -> age + 1 end)
1016+ %{"john" => %{age: 28}, "meg" => %{age: 24}}
1017+ iex> put_in(users, [Access.values(), :planet], "Earth")
1018+ %{"john" => %{age: 27, planet: "Earth"}, "meg" => %{age: 23, planet: "Earth"}}
1019+
1020+ By returning `:pop` from an accessor function, you can remove the accessed key and value
1021+ from the map:
1022+
1023+ iex> require Integer
1024+ iex> numbers = %{one: 1, two: 2, three: 3, four: 4}
1025+ iex> get_and_update_in(numbers, [Access.values()], fn num ->
1026+ ...> if Integer.is_even(num), do: :pop, else: {num, to_string(num)}
1027+ ...> end)
1028+ {[1, 2, 3, 4], %{one: "1", three: "3"}}
1029+
1030+ An error is raised if the accessed structure is not a map:
1031+
1032+ iex> get_in([1, 2, 3], [Access.values()])
1033+ ** (RuntimeError) Access.values/0 expected a map, got: [1, 2, 3]
1034+ """
1035+ @ spec values ( ) :: Access . access_fun ( data :: map ( ) , current_value :: list ( ) )
1036+ def values do
1037+ & values / 3
1038+ end
1039+
1040+ defp values ( :get , data = % { } , next ) do
1041+ Enum . map ( data , fn { _key , value } -> next . ( value ) end )
1042+ end
1043+
1044+ defp values ( :get_and_update , data = % { } , next ) do
1045+ { reverse_gets , updated_data } =
1046+ Enum . reduce ( data , { [ ] , % { } } , fn { key , value } , { gets , data_acc } ->
1047+ case next . ( value ) do
1048+ { get , update } -> { [ get | gets ] , Map . put ( data_acc , key , update ) }
1049+ :pop -> { [ value | gets ] , data_acc }
1050+ end
1051+ end )
1052+
1053+ { Enum . reverse ( reverse_gets ) , updated_data }
1054+ end
1055+
1056+ defp values ( _op , data , _next ) do
1057+ raise "Access.values/0 expected a map, got: #{ inspect ( data ) } "
1058+ end
1059+
1060+ @ doc """
1061+ Returns a function that accesses all keys in a map.
1062+
1063+ The returned function is typically passed as an accessor to `Kernel.get_in/2`,
1064+ `Kernel.get_and_update_in/3`, and friends.
1065+
1066+ Beware that returning the same key multiple times in `Kernel.put_in/3`, `Kernel.update_in/3`,
1067+ or `Kernel.get_and_update_in/3` will cause the previous values of the same key to be
1068+ overwritten as maps cannot have duplicate keys.
1069+
1070+ See `values/0` for a function that accesses all values in a map.
1071+
1072+ ## Examples
1073+
1074+ iex> data = %{users: %{"john" => %{age: 27}, "meg" => %{age: 23}}}
1075+ iex> get_in(data, [:users, Access.keys()])
1076+ ["john", "meg"]
1077+ iex> update_in(data, [:users, Access.keys()], fn name -> String.upcase(name) end)
1078+ %{users: %{"JOHN" => %{age: 27}, "MEG" => %{age: 23}}}
1079+
1080+ By returning `:pop` from an accessor function, you can remove the accessed key and value
1081+ from the map:
1082+
1083+ iex> require Integer
1084+ iex> numbers = %{1 => "one", 2 => "two", 3 => "three", 4 => "four"}
1085+ iex> get_and_update_in(numbers, [Access.keys()], fn num ->
1086+ ...> if Integer.is_even(num), do: :pop, else: {num, to_string(num)}
1087+ ...> end)
1088+ {[1, 2, 3, 4], %{"1" => "one", "3" => "three"}}
1089+
1090+ An error is raised if the accessed structure is not a map:
1091+
1092+ iex> get_in([1, 2, 3], [Access.keys()])
1093+ ** (RuntimeError) Access.keys/0 expected a map, got: [1, 2, 3]
1094+ """
1095+ @ spec keys ( ) :: Access . access_fun ( data :: map ( ) , current_value :: list ( ) )
1096+ def keys do
1097+ & keys / 3
1098+ end
1099+
1100+ defp keys ( :get , data = % { } , next ) do
1101+ Enum . map ( data , fn { key , _value } -> next . ( key ) end )
1102+ end
1103+
1104+ defp keys ( :get_and_update , data = % { } , next ) do
1105+ { reverse_gets , updated_data } =
1106+ Enum . reduce ( data , { [ ] , % { } } , fn { key , value } , { gets , data_acc } ->
1107+ case next . ( key ) do
1108+ { get , update } -> { [ get | gets ] , Map . put ( data_acc , update , value ) }
1109+ :pop -> { [ key | gets ] , data_acc }
1110+ end
1111+ end )
1112+
1113+ { Enum . reverse ( reverse_gets ) , updated_data }
1114+ end
1115+
1116+ defp keys ( _op , data , _next ) do
1117+ raise "Access.keys/0 expected a map, got: #{ inspect ( data ) } "
1118+ end
1119+
10021120 defp normalize_range ( % Range { first: first , last: last , step: step } , list )
10031121 when first < 0 or last < 0 do
10041122 count = length ( list )
0 commit comments