In Julia, the recommended way to iterate over all indices of an AbstractArray
is to use eachindex
, e.g.,
for i in eachindex(a)
do_something(a[i], i)
end
In contrast to 1:length(a)
, eachindex(a)
supports arrays with unconventional indexing, i.e., indices not starting at 1
. Additionally, it is more efficient for arrays with slow linear indexing.
If I want to skip the first index I can use Iterators.drop(eachindex(a), 1)
(is there a better way?) but how do I skip the last one in a generic way?
A "front" iterator is relatively simple and generally useful. Edit: it's also totally overkill to define it in full generality just for this case. It's much easier and simpler to rely on Base's builtins with a definition like:
front(itr, n=1) = Iterators.take(itr, length(itr)-n)
This will work for all iterators with length
defined — which will include everything that eachindex
will return.
Alternatively, you can define a specialized iterator from first principles that doesn't depend upon length
being defined. I'm not aware of such a structure in any existing packages. Using Julia 0.6, an implementation could look like:
struct Front{T}
itr::T
end
# Basic iterator definition
function Base.start(f::Front)
s = start(f.itr)
done(f.itr, s) && throw(ArgumentError("cannot take the front of an empty iterator"))
return next(f.itr, s)
end
function Base.next(f::Front, state)
val, s = state
return val, next(f.itr, s)
end
Base.done(f::Front, state) = done(f.itr, state[2])
# Inherit traits as appropriate
Base.iteratorsize(::Type{Front{T}}) where {T} = _dropshape(Base.iteratorsize(T))
_dropshape(x) = x
_dropshape(::Base.HasShape) = Base.HasLength()
Base.iteratoreltype(::Type{Front{T}}) where {T} = Base.iteratoreltype(T)
Base.length(f::Front) = length(f.itr) - 1
Base.eltype(f::Front{T}) where {T} = eltype(T)
Now:
julia> collect(Front(eachindex(rand(5))))
4-element Array{Int64,1}:
1
2
3
4
julia> collect(Front(eachindex(sprand(3, 2, .2))))
5-element Array{CartesianIndex{2},1}:
CartesianIndex{2}((1, 1))
CartesianIndex{2}((2, 1))
CartesianIndex{2}((3, 1))
CartesianIndex{2}((1, 2))
CartesianIndex{2}((2, 2))
Another way to define @MattB.'s Front
is
front(itr,n=1) = (first(x) for x in Iterators.partition(itr,n+1,1))
This also gives:
julia> front(eachindex([1,2,3,4,5]))|>collect
4-element Array{Int64,1}:
1
2
3
4
and as a bonus:
julia> front(eachindex([1,2,3,4,5]),2)|>collect
3-element Array{Int64,1}:
1
2
3
the corresponding iterator to drop(eachindex([1,2,3,4,5]),2)
.
There's also the following:
for I in CartesianRange(Base.front(indices(A)))
@show I A[I, :]
end
On A = reshape(1:27, 3, 3, 3)
this yields
I = CartesianIndex{2}((1,1))
A[I,:] = [1,10,19]
I = CartesianIndex{2}((2,1))
A[I,:] = [2,11,20]
I = CartesianIndex{2}((3,1))
A[I,:] = [3,12,21]
I = CartesianIndex{2}((1,2))
A[I,:] = [4,13,22]
I = CartesianIndex{2}((2,2))
A[I,:] = [5,14,23]
I = CartesianIndex{2}((3,2))
A[I,:] = [6,15,24]
I = CartesianIndex{2}((1,3))
A[I,:] = [7,16,25]
I = CartesianIndex{2}((2,3))
A[I,:] = [8,17,26]
I = CartesianIndex{2}((3,3))
A[I,:] = [9,18,27]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With