Skip to content

Commit d53a315

Browse files
author
José Valim
committed
Merge pull request #2414 from christhekeele/empty-range-slice
Ensures Enum.slice always returns a list.
2 parents 8d2199c + d35d03a commit d53a315

File tree

2 files changed

+61
-34
lines changed

2 files changed

+61
-34
lines changed

lib/elixir/lib/enum.ex

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,18 +1417,33 @@ defmodule Enum do
14171417
@doc """
14181418
Returns a subset list of the given collection. Drops elements
14191419
until element position `start`, then takes `count` elements.
1420+
1421+
If the count is greater than collection length, it returns as
1422+
much as possible. If zero, then it returns `[]`.
14201423
14211424
## Examples
14221425
14231426
iex> Enum.slice(1..100, 5, 10)
14241427
[6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
14251428
1429+
iex> Enum.slice(1..10, 5, 100)
1430+
[6, 7, 8, 9, 10]
1431+
1432+
iex> Enum.slice(1..10, 5, 0)
1433+
[]
1434+
14261435
"""
14271436
@spec slice(t, integer, non_neg_integer) :: list
1437+
1438+
def slice(_coll, _start, 0), do: []
14281439

14291440
def slice(coll, start, count) when start < 0 do
14301441
{list, new_start} = enumerate_and_count(coll, start)
1431-
if new_start >= 0, do: slice(list, new_start, count)
1442+
if new_start >= 0 do
1443+
slice(list, new_start, count)
1444+
else
1445+
[]
1446+
end
14321447
end
14331448

14341449
def slice(coll, start, count) when is_list(coll) and start >= 0 and count > 0 do
@@ -1445,15 +1460,7 @@ defmodule Enum do
14451460
{:halt, {start, count, [entry|list]}}
14461461
end) |> elem(1)
14471462

1448-
if start <= 0, do: :lists.reverse(list)
1449-
end
1450-
1451-
def slice(coll, start, 0) do
1452-
res =
1453-
Enumerable.reduce(coll, {:cont, start}, fn _, start ->
1454-
if start > 0, do: {:cont, start-1}, else: {:halt, []}
1455-
end) |> elem(1)
1456-
if is_list(res), do: res
1463+
:lists.reverse(list)
14571464
end
14581465

14591466
@doc """
@@ -1468,17 +1475,31 @@ defmodule Enum do
14681475
The first position (after adding count to negative positions) must be smaller
14691476
or equal to the last position.
14701477
1478+
If the start of the range is not a valid offset for the given
1479+
collection or if the range is in reverse order, returns `[]`.
1480+
14711481
## Examples
14721482
14731483
iex> Enum.slice(1..100, 5..10)
14741484
[6, 7, 8, 9, 10, 11]
14751485
1486+
iex> Enum.slice(1..10, 5..20)
1487+
[6, 7, 8, 9, 10]
1488+
1489+
iex> Enum.slice(1..10, 11..20)
1490+
[]
1491+
1492+
iex> Enum.slice(1..10, 6..5)
1493+
[]
1494+
14761495
"""
14771496
@spec slice(t, Range.t) :: list
14781497
def slice(coll, first..last) when first >= 0 and last >= 0 do
14791498
# Simple case, which works on infinite collections
14801499
if last - first >= 0 do
14811500
slice(coll, first, last - first + 1)
1501+
else
1502+
[]
14821503
end
14831504
end
14841505

@@ -1489,6 +1510,8 @@ defmodule Enum do
14891510
length = corr_last - corr_first + 1
14901511
if corr_first >= 0 and length > 0 do
14911512
slice(list, corr_first, length)
1513+
else
1514+
[]
14921515
end
14931516
end
14941517

@@ -2143,11 +2166,11 @@ defmodule Enum do
21432166
## slice
21442167

21452168
defp do_slice([], start, _count) do
2146-
if start == 0, do: []
2169+
[]
21472170
end
21482171

21492172
defp do_slice(list, start, 0) do
2150-
if start < length(list), do: []
2173+
[]
21512174
end
21522175

21532176
defp do_slice([h|t], 0, count) do

lib/elixir/test/elixir/enum_test.exs

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -426,11 +426,12 @@ defmodule EnumTest.List do
426426
assert Enum.slice([1,2,3,4,5], 1, 2) == [2, 3]
427427
assert Enum.slice([1,2,3,4,5], 1, 0) == []
428428
assert Enum.slice([1,2,3,4,5], 2, 5) == [3, 4, 5]
429+
assert Enum.slice([1,2,3,4,5], 2, 6) == [3, 4, 5]
429430
assert Enum.slice([1,2,3,4,5], 5, 5) == []
430-
assert Enum.slice([1,2,3,4,5], 6, 5) == nil
431-
assert Enum.slice([1,2,3,4,5], 6, 0) == nil
432-
assert Enum.slice([1,2,3,4,5], -6, 0) == nil
433-
assert Enum.slice([1,2,3,4,5], -6, 5) == nil
431+
assert Enum.slice([1,2,3,4,5], 6, 5) == []
432+
assert Enum.slice([1,2,3,4,5], 6, 0) == []
433+
assert Enum.slice([1,2,3,4,5], -6, 0) == []
434+
assert Enum.slice([1,2,3,4,5], -6, 5) == []
434435
assert Enum.slice([1,2,3,4,5], -2, 5) == [4, 5]
435436
assert Enum.slice([1,2,3,4,5], -3, 1) == [3]
436437
end
@@ -440,18 +441,19 @@ defmodule EnumTest.List do
440441
assert Enum.slice([1,2,3,4,5], 0..1) == [1, 2]
441442
assert Enum.slice([1,2,3,4,5], 0..2) == [1, 2, 3]
442443
assert Enum.slice([1,2,3,4,5], 1..2) == [2, 3]
443-
assert Enum.slice([1,2,3,4,5], 1..0) == nil
444+
assert Enum.slice([1,2,3,4,5], 1..0) == []
444445
assert Enum.slice([1,2,3,4,5], 2..5) == [3, 4, 5]
446+
assert Enum.slice([1,2,3,4,5], 2..6) == [3, 4, 5]
445447
assert Enum.slice([1,2,3,4,5], 4..4) == [5]
446448
assert Enum.slice([1,2,3,4,5], 5..5) == []
447-
assert Enum.slice([1,2,3,4,5], 6..5) == nil
448-
assert Enum.slice([1,2,3,4,5], 6..0) == nil
449-
assert Enum.slice([1,2,3,4,5], -6..0) == nil
450-
assert Enum.slice([1,2,3,4,5], -6..5) == nil
449+
assert Enum.slice([1,2,3,4,5], 6..5) == []
450+
assert Enum.slice([1,2,3,4,5], 6..0) == []
451+
assert Enum.slice([1,2,3,4,5], -6..0) == []
452+
assert Enum.slice([1,2,3,4,5], -6..5) == []
451453
assert Enum.slice([1,2,3,4,5], -5..-1) == [1, 2, 3, 4, 5]
452454
assert Enum.slice([1,2,3,4,5], -5..-3) == [1, 2, 3]
453-
assert Enum.slice([1,2,3,4,5], -6..-1) == nil
454-
assert Enum.slice([1,2,3,4,5], -6..-3) == nil
455+
assert Enum.slice([1,2,3,4,5], -6..-1) == []
456+
assert Enum.slice([1,2,3,4,5], -6..-3) == []
455457
end
456458
end
457459

@@ -759,11 +761,12 @@ defmodule EnumTest.Range do
759761
assert Enum.slice(1..5, 1, 2) == [2, 3]
760762
assert Enum.slice(1..5, 1, 0) == []
761763
assert Enum.slice(1..5, 2, 5) == [3, 4, 5]
764+
assert Enum.slice(1..5, 2, 6) == [3, 4, 5]
762765
assert Enum.slice(1..5, 5, 5) == []
763-
assert Enum.slice(1..5, 6, 5) == nil
764-
assert Enum.slice(1..5, 6, 0) == nil
765-
assert Enum.slice(1..5, -6, 0) == nil
766-
assert Enum.slice(1..5, -6, 5) == nil
766+
assert Enum.slice(1..5, 6, 5) == []
767+
assert Enum.slice(1..5, 6, 0) == []
768+
assert Enum.slice(1..5, -6, 0) == []
769+
assert Enum.slice(1..5, -6, 5) == []
767770
assert Enum.slice(1..5, -2, 5) == [4, 5]
768771
assert Enum.slice(1..5, -3, 1) == [3]
769772
end
@@ -773,18 +776,19 @@ defmodule EnumTest.Range do
773776
assert Enum.slice(1..5, 0..1) == [1, 2]
774777
assert Enum.slice(1..5, 0..2) == [1, 2, 3]
775778
assert Enum.slice(1..5, 1..2) == [2, 3]
776-
assert Enum.slice(1..5, 1..0) == nil
779+
assert Enum.slice(1..5, 1..0) == []
777780
assert Enum.slice(1..5, 2..5) == [3, 4, 5]
781+
assert Enum.slice(1..5, 2..6) == [3, 4, 5]
778782
assert Enum.slice(1..5, 4..4) == [5]
779783
assert Enum.slice(1..5, 5..5) == []
780-
assert Enum.slice(1..5, 6..5) == nil
781-
assert Enum.slice(1..5, 6..0) == nil
782-
assert Enum.slice(1..5, -6..0) == nil
783-
assert Enum.slice(1..5, -6..5) == nil
784+
assert Enum.slice(1..5, 6..5) == []
785+
assert Enum.slice(1..5, 6..0) == []
786+
assert Enum.slice(1..5, -6..0) == []
787+
assert Enum.slice(1..5, -6..5) == []
784788
assert Enum.slice(1..5, -5..-1) == [1, 2, 3, 4, 5]
785789
assert Enum.slice(1..5, -5..-3) == [1, 2, 3]
786-
assert Enum.slice(1..5, -6..-1) == nil
787-
assert Enum.slice(1..5, -6..-3) == nil
790+
assert Enum.slice(1..5, -6..-1) == []
791+
assert Enum.slice(1..5, -6..-3) == []
788792
end
789793

790794
test :sort do

0 commit comments

Comments
 (0)