@@ -2143,28 +2143,66 @@ defmodule Enum do
2143
2143
2144
2144
@ doc """
2145
2145
Returns a tuple with the minimal and the maximal elements in the
2146
- enumerable according to Erlang's term ordering .
2146
+ enumerable.
2147
2147
2148
- If multiple elements are considered maximal or minimal, the first one
2149
- that was found is returned.
2150
-
2151
- Calls the provided `empty_fallback` function and returns its value if
2152
- `enumerable` is empty. The default `empty_fallback` raises `Enum.EmptyError`.
2148
+ By default, the comparison is done with the `<` sorter function,
2149
+ as the function must not return true for equal elements.
2153
2150
2154
2151
## Examples
2155
2152
2156
2153
iex> Enum.min_max([2, 3, 1])
2157
2154
{1, 3}
2158
2155
2156
+ iex> Enum.min_max(["foo", "bar", "baz"])
2157
+ {"bar", "foo"}
2158
+
2159
2159
iex> Enum.min_max([], fn -> {nil, nil} end)
2160
2160
{nil, nil}
2161
2161
2162
+ The fact this function uses Erlang's term ordering means that the
2163
+ comparison is structural and not semantic. Therefore, if you want
2164
+ to compare structs, most structs provide a "compare" function, such as
2165
+ `Date.compare/2`, which receives two structs and returns `:lt` (less-than),
2166
+ `:eq` (equal to), and `:gt` (greater-than). If you pass a module as the
2167
+ sorting function, Elixir will automatically use the `compare/2` function
2168
+ of said module:
2169
+
2170
+ iex> dates = [
2171
+ ...> ~D[2019-01-01],
2172
+ ...> ~D[2020-01-01],
2173
+ ...> ~D[2018-01-01]
2174
+ ...> ]
2175
+ iex> Enum.min_max(dates, Date)
2176
+ {~D[2018-01-01], ~D[2020-01-01]}
2177
+
2178
+ You can also pass a custom sorting function:
2179
+
2180
+ iex> Enum.min_max([2, 3, 1], &>/2)
2181
+ {3, 1}
2182
+
2183
+ Finally, if you don't want to raise on empty enumerables, you can pass
2184
+ the empty fallback:
2185
+
2186
+ iex> Enum.min_max([], fn -> nil end)
2187
+ nil
2188
+
2162
2189
"""
2163
- @ spec min_max ( t , ( -> empty_result ) ) :: { element , element } | empty_result
2190
+ @ spec min_max ( t , ( element , element -> boolean ) | module ( ) ) ::
2191
+ { element , element } | empty_result
2192
+ when empty_result: any
2193
+ @ spec min_max (
2194
+ t ,
2195
+ ( element , element -> boolean ) | module ( ) ,
2196
+ ( -> empty_result )
2197
+ ) :: { element , element } | empty_result
2164
2198
when empty_result: any
2165
- def min_max ( enumerable , empty_fallback \\ fn -> raise Enum.EmptyError end )
2166
2199
2167
- def min_max ( first .. last // step = range , empty_fallback ) when is_function ( empty_fallback , 0 ) do
2200
+ def min_max ( enumerable ) do
2201
+ min_max ( enumerable , fn -> raise Enum.EmptyError end )
2202
+ end
2203
+
2204
+ def min_max ( first .. last // step = range , empty_fallback )
2205
+ when is_function ( empty_fallback , 0 ) do
2168
2206
case Range . size ( range ) do
2169
2207
0 ->
2170
2208
empty_fallback . ( )
@@ -2175,11 +2213,39 @@ defmodule Enum do
2175
2213
end
2176
2214
end
2177
2215
2178
- def min_max ( enumerable , empty_fallback ) when is_function ( empty_fallback , 0 ) do
2216
+ def min_max ( enumerable , empty_fallback )
2217
+ when is_function ( empty_fallback , 0 ) do
2218
+ min_max ( enumerable , & < / 2 , empty_fallback )
2219
+ end
2220
+
2221
+ def min_max ( enumerable , sorter ) when is_atom ( sorter ) do
2222
+ min_max ( enumerable , min_max_sort_fun ( sorter ) )
2223
+ end
2224
+
2225
+ def min_max ( enumerable , sorter ) when is_function ( sorter , 2 ) do
2226
+ min_max ( enumerable , sorter , fn -> raise Enum.EmptyError end )
2227
+ end
2228
+
2229
+ def min_max ( enumerable , sorter , empty_fallback )
2230
+ when is_atom ( sorter ) and is_function ( empty_fallback , 0 ) do
2231
+ min_max ( enumerable , min_max_sort_fun ( sorter ) , empty_fallback )
2232
+ end
2233
+
2234
+ def min_max ( enumerable , sorter , empty_fallback )
2235
+ when is_function ( sorter , 2 ) and is_function ( empty_fallback , 0 ) do
2179
2236
first_fun = & [ & 1 | & 1 ]
2180
2237
2181
- reduce_fun = fn entry , [ min | max ] ->
2182
- [ Kernel . min ( min , entry ) | Kernel . max ( max , entry ) ]
2238
+ reduce_fun = fn entry , [ min | max ] = acc ->
2239
+ cond do
2240
+ sorter . ( entry , min ) ->
2241
+ [ entry | max ]
2242
+
2243
+ sorter . ( max , entry ) ->
2244
+ [ min | entry ]
2245
+
2246
+ true ->
2247
+ acc
2248
+ end
2183
2249
end
2184
2250
2185
2251
case reduce_by ( enumerable , first_fun , reduce_fun ) do
@@ -2200,8 +2266,8 @@ defmodule Enum do
2200
2266
Returns a tuple with the minimal and the maximal elements in the
2201
2267
enumerable as calculated by the given function.
2202
2268
2203
- If multiple elements are considered maximal or minimal, the first one
2204
- that was found is returned .
2269
+ By default, the comparison is done with the `<` sorter function,
2270
+ as the function must not return true for equal elements .
2205
2271
2206
2272
## Examples
2207
2273
@@ -2259,7 +2325,7 @@ defmodule Enum do
2259
2325
2260
2326
def min_max_by ( enumerable , fun , sorter , empty_fallback )
2261
2327
when is_function ( fun , 1 ) and is_atom ( sorter ) and is_function ( empty_fallback , 0 ) do
2262
- min_max_by ( enumerable , fun , min_max_by_sort_fun ( sorter ) , empty_fallback )
2328
+ min_max_by ( enumerable , fun , min_max_sort_fun ( sorter ) , empty_fallback )
2263
2329
end
2264
2330
2265
2331
def min_max_by ( enumerable , fun , sorter , empty_fallback )
@@ -2290,7 +2356,7 @@ defmodule Enum do
2290
2356
end
2291
2357
end
2292
2358
2293
- defp min_max_by_sort_fun ( module ) when is_atom ( module ) , do: & ( module . compare ( & 1 , & 2 ) == :lt )
2359
+ defp min_max_sort_fun ( module ) when is_atom ( module ) , do: & ( module . compare ( & 1 , & 2 ) == :lt )
2294
2360
2295
2361
@ doc """
2296
2362
Splits the `enumerable` in two lists according to the given function `fun`.
0 commit comments