Skip to content

Commit 8edf880

Browse files
committed
Add enumerate(::IndexMethod, A) for index/value iteration
`enumerate(A)` doesn't guarantee that the counter corresponds to the index; so when you need an index, call this method.
1 parent e6165fd commit 8edf880

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

base/abstractarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1644,7 +1644,7 @@ _sub2ind_vec(inds, out, i) = (@_inline_meta; sub2ind(inds, out...))
16441644
function ind2sub{N}(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer})
16451645
M = length(ind)
16461646
t = ntuple(n->similar(ind),Val{N})
1647-
for (i,idx) in enumerate(ind) # FIXME: change to eachindexvalue
1647+
for (i,idx) in enumerate(IndexLinear(), ind)
16481648
sub = ind2sub(inds, idx)
16491649
for j = 1:N
16501650
t[j][i] = sub[j]

base/iterators.jl

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ and `x` is the `i`th value from the given iterator. It's useful when
4040
you need not only the values `x` over which you are iterating, but
4141
also the number of iterations so far. Note that `i` may not be valid
4242
for indexing `iter`; it's also possible that `x != iter[i]`, if `iter`
43-
has indices that do not start at 1.
43+
has indices that do not start at 1. See the `enumerate(IndexLinear(),
44+
iter)` method if you want to ensure that `i` is an index.
4445
4546
```jldoctest
4647
julia> a = ["a", "b", "c"];
@@ -69,6 +70,77 @@ eltype{I}(::Type{Enumerate{I}}) = Tuple{Int, eltype(I)}
6970
iteratorsize{I}(::Type{Enumerate{I}}) = iteratorsize(I)
7071
iteratoreltype{I}(::Type{Enumerate{I}}) = iteratoreltype(I)
7172

73+
struct IndexValue{I,A<:AbstractArray}
74+
data::A
75+
itr::I
76+
end
77+
78+
"""
79+
enumerate(IndexLinear(), A)
80+
enumerate(IndexCartesian(), A)
81+
enumerate(IndexMethod(A), A)
82+
83+
An iterator that accesses each element of the array `A`, returning
84+
`(i, x)`, where `i` is the index for the element and `x = A[i]`. This
85+
is similar to `enumerate(A)`, except `i` will always be a valid index
86+
for `A`. Like `enumerate`, the bounds-check that would otherwise
87+
occur for `A[i]` is omitted.
88+
89+
Specifying specifying `IndexLinear()` ensures that `i` will be an
90+
integer; specifying `IndexCartesian()` ensures that `i` will be a
91+
`CartesianIndex`; specifying `IndexMethod(A)` chooses whichever has
92+
been defined as the native indexing method for array `A`.
93+
94+
```jldoctest
95+
julia> A = ["a" "d"; "b" "e"; "c" "f"];
96+
97+
julia> for (index, value) in enumerate(IndexMethod(A), A)
98+
println("\$index \$value")
99+
end
100+
1 a
101+
2 b
102+
3 c
103+
4 d
104+
5 e
105+
6 f
106+
107+
julia> S = view(A, 1:2, :);
108+
109+
julia> for (index, value) in enumerate(IndexMethod(S), S)
110+
println("\$index \$value")
111+
end
112+
CartesianIndex{2}((1, 1)) a
113+
CartesianIndex{2}((2, 1)) b
114+
CartesianIndex{2}((1, 2)) d
115+
CartesianIndex{2}((2, 2)) e
116+
```
117+
118+
Note that for a vector `v`, `enumerate(v)` returns `i` as a *counter*
119+
(always starting at 1), whereas `enumerate(IndexLinear(), v)` returns
120+
`i` as an *index* (starting at the first index of `v`, which may or
121+
may not be 1).
122+
123+
See also: [`IndexMethod`](@ref), [`indices`](@ref).
124+
"""
125+
enumerate(::IndexLinear, A::AbstractArray) = IndexValue(A, linearindices(A))
126+
enumerate(::IndexCartesian, A::AbstractArray) = IndexValue(A, CartesianRange(indices(A)))
127+
128+
length(v::IndexValue) = length(v.itr)
129+
indices(v::IndexValue) = indices(v.itr)
130+
size(v::IndexValue) = size(v.itr)
131+
@inline start(v::IndexValue) = start(v.itr)
132+
@inline function next(v::IndexValue, state)
133+
indx, n = next(v.itr, state)
134+
@inbounds item = v.data[indx]
135+
(indx, item), n
136+
end
137+
@inline done(v::IndexValue, state) = done(v.itr, state)
138+
139+
eltype{I,A}(::Type{IndexValue{I,A}}) = Tuple{eltype(I), eltype(A)}
140+
141+
iteratorsize{I}(::Type{IndexValue{I}}) = iteratorsize(I)
142+
iteratoreltype{I}(::Type{IndexValue{I}}) = iteratoreltype(I)
143+
72144
# zip
73145

74146
abstract type AbstractZipIterator end

test/arrayops.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,15 @@ end
11211121
end
11221122
end
11231123

1124+
@testset "eachindexvalue" begin
1125+
A14 = [11 13; 12 14]
1126+
R = CartesianRange(indices(A14))
1127+
@test [a for (a,b) in enumerate(IndexLinear(), A14)] == [1,2,3,4]
1128+
@test [a for (a,b) in enumerate(IndexCartesian(), A14)] == vec(collect(R))
1129+
@test [b for (a,b) in enumerate(IndexLinear(), A14)] == [11,12,13,14]
1130+
@test [b for (a,b) in enumerate(IndexCartesian(), A14)] == [11,12,13,14]
1131+
end
1132+
11241133
@testset "reverse" begin
11251134
@test reverse([2,3,1]) == [1,3,2]
11261135
@test reverse([1:10;],1,4) == [4,3,2,1,5,6,7,8,9,10]

0 commit comments

Comments
 (0)