Skip to content

Commit 7d7c2ef

Browse files
committed
Generalise two-argument first and last to any iterable
1 parent 0e0eced commit 7d7c2ef

File tree

3 files changed

+33
-18
lines changed

3 files changed

+33
-18
lines changed

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ Standard library changes
6565
* `unique(f, itr; seen=Set{T}())` now allows you to declare the container type used for
6666
keeping track of values returned by `f` on elements of `itr` ([#36280]).
6767
* `Libdl` has been moved to `Base.Libc.Libdl`, however it is still accessible as an stdlib ([#35628]).
68+
* `first` and `last` functions now accept an integer as second argument to get that many
69+
leading or trailing elements of any iterable ([#34868]).
6870

6971
#### LinearAlgebra
7072
* New method `LinearAlgebra.issuccess(::CholeskyPivoted)` for checking whether pivoted Cholesky factorization was successful ([#36002]).

base/abstractarray.jl

+18-6
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,10 @@ function first(itr)
344344
end
345345

346346
"""
347-
first(v::AbstractVector, n::Integer)
347+
first(itr, n::Integer)
348348
349-
Get the first `n` elements of vector `v`, or fewer elements if `v` is not long enough.
349+
Get the first `n` elements of the iterable collection `itr`, or fewer elements if `v` is not
350+
long enough.
350351
351352
# Examples
352353
```jldoctest
@@ -362,7 +363,12 @@ julia> first(Bool[], 1)
362363
0-element Array{Bool,1}
363364
```
364365
"""
365-
first(v::AbstractVector, n::Integer) = @inbounds v[begin:min(begin + n - 1, end)]
366+
first(itr, n::Integer) = collect(Iterators.take(itr, n))
367+
# Faster method for vectors
368+
function first(v::AbstractVector, n::Integer)
369+
n < 0 && throw(ArgumentError("Number of elements must be nonnegative"))
370+
@inbounds v[begin:min(begin + n - 1, end)]
371+
end
366372

367373
"""
368374
last(coll)
@@ -383,9 +389,10 @@ julia> last([1; 2; 3; 4])
383389
last(a) = a[end]
384390

385391
"""
386-
last(v::AbstractVector, n::Integer)
392+
last(itr, n::Integer)
387393
388-
Get the last `n` elements of vector `v`, or fewer elements if `v` is not long enough.
394+
Get the last `n` elements of the iterable collection `itr`, or fewer elements if `v` is not
395+
long enough.
389396
390397
# Examples
391398
```jldoctest
@@ -401,7 +408,12 @@ julia> last(Float64[], 1)
401408
0-element Array{Float64,1}
402409
```
403410
"""
404-
last(v::AbstractArray, n::Integer) = @inbounds v[max(begin, end - n + 1):end]
411+
last(itr, n::Integer) = reverse!(collect(Iterators.take(Iterators.reverse(itr), n)))
412+
# Faster method for arrays
413+
function last(v::AbstractArray, n::Integer)
414+
n < 0 && throw(ArgumentError("Number of elements must be nonnegative"))
415+
@inbounds v[max(begin, end - n + 1):end]
416+
end
405417

406418
"""
407419
strides(A)

test/abstractarray.jl

+13-12
Original file line numberDiff line numberDiff line change
@@ -1127,16 +1127,17 @@ end
11271127
end
11281128
end
11291129

1130-
@testset "first/last n elements of vector" begin
1131-
v = [1, 13, 42]
1132-
@test first(v, -2) == []
1133-
@test first(v, 2) == v[1:2]
1134-
@test first(v, 100) == v
1135-
@test first(v, 100) !== v
1136-
@test first(v, 1) != v[1]
1137-
@test last(v, -2) == []
1138-
@test last(v, 2) == v[end-1:end]
1139-
@test last(v, 100) == v
1140-
@test last(v, 100) !== v
1141-
@test last(v, 1) != v[end]
1130+
@testset "first/last n elements of $(typeof(itr))" for itr in (collect(1:9),
1131+
[1 4 7; 2 5 8; 3 6 9],
1132+
ntuple(identity, 9))
1133+
@test first(itr, 6) == [itr[1:6]...]
1134+
@test first(itr, 25) == [itr[:]...]
1135+
@test first(itr, 25) !== itr
1136+
@test first(itr, 1) == [itr[1]]
1137+
@test_throws ArgumentError first(itr, -6)
1138+
@test last(itr, 6) == [itr[end-5:end]...]
1139+
@test last(itr, 25) == [itr[:]...]
1140+
@test last(itr, 25) !== itr
1141+
@test last(itr, 1) == [itr[end]]
1142+
@test_throws ArgumentError last(itr, -6)
11421143
end

0 commit comments

Comments
 (0)