-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change find() to return the same index type as pairs() #24774
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
This does not change anything for AbstractVectors and general iterables, which continue to use linear indices. For other AbstractArrays, return CartesianIndexes (rather than linear indices). For Dicts, return keys (previously not supported at all). Relying on collect() to choose the return element type allows supporting any definition of pairs(), including that for Dict, which creates a standard Generator for which eltype() returns Any.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1720,48 +1720,59 @@ findlast(testf::Function, A) = findprev(testf, A, endof(A)) | |
""" | ||
find(f::Function, A) | ||
|
||
Return a vector `I` of the linear indices of `A` where `f(A[I])` returns `true`. | ||
Return a vector `I` of the indices or keys of `A` where `f(A[I])` returns `true`. | ||
If there are no such elements of `A`, return an empty array. | ||
|
||
Indices or keys are of the same type as those returned by [`keys(A)`](@ref) | ||
and [`pairs(A)`](@ref) for `AbstractArray` and `Associative` objects, | ||
and are linear indices of type `Int` for other iterables. | ||
|
||
# Examples | ||
```jldoctest | ||
julia> x = [1, 3, 4] | ||
3-element Array{Int64,1}: | ||
1 | ||
3 | ||
4 | ||
|
||
julia> find(isodd, x) | ||
2-element Array{Int64,1}: | ||
1 | ||
2 | ||
|
||
julia> A = [1 2 0; 3 4 0] | ||
2×3 Array{Int64,2}: | ||
1 2 0 | ||
3 4 0 | ||
|
||
julia> find(isodd, A) | ||
2-element Array{Int64,1}: | ||
1 | ||
2 | ||
2-element Array{CartesianIndex{2},1}: | ||
CartesianIndex(1, 1) | ||
CartesianIndex(2, 1) | ||
|
||
julia> find(!iszero, A) | ||
4-element Array{Int64,1}: | ||
1 | ||
2 | ||
3 | ||
4 | ||
4-element Array{CartesianIndex{2},1}: | ||
CartesianIndex(1, 1) | ||
CartesianIndex(2, 1) | ||
CartesianIndex(1, 2) | ||
CartesianIndex(2, 2) | ||
|
||
julia> d = Dict(:A => 10, :B => -1, :C => 0) | ||
Dict{Symbol,Int64} with 3 entries: | ||
:A => 10 | ||
:B => -1 | ||
:C => 0 | ||
|
||
julia> find(x -> x >= 0, d) | ||
2-element Array{Symbol,1}: | ||
:A | ||
:C | ||
|
||
julia> find(isodd, [2, 4]) | ||
0-element Array{Int64,1} | ||
``` | ||
""" | ||
function find(testf::Function, A) | ||
# use a dynamic-length array to store the indices, then copy to a non-padded | ||
# array for the return | ||
tmpI = Vector{Int}() | ||
inds = _index_remapper(A) | ||
for (i,a) = enumerate(A) | ||
if testf(a) | ||
push!(tmpI, inds[i]) | ||
end | ||
end | ||
I = Vector{Int}(uninitialized, length(tmpI)) | ||
copyto!(I, tmpI) | ||
return I | ||
end | ||
_index_remapper(A::AbstractArray) = linearindices(A) | ||
_index_remapper(iter) = OneTo(typemax(Int)) # safe for objects that don't implement length | ||
find(testf::Function, A) = collect(first(p) for p in _pairs(A) if testf(last(p))) | ||
|
||
_pairs(A::Union{AbstractArray, AbstractDict}) = pairs(A) | ||
_pairs(iter) = zip(OneTo(typemax(Int)), iter) # safe for objects that don't implement length | ||
|
||
""" | ||
find(A) | ||
|
@@ -1786,22 +1797,10 @@ julia> find(falses(3)) | |
``` | ||
""" | ||
function find(A) | ||
nnzA = count(t -> t != 0, A) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Due to issues with the inference of the returned eltype (see commit message), I've taken a radical approach simply using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Historically it was worth it, but might be worth benchmarking again. See also https://discourse.julialang.org/t/push-and-interfacing-to-the-runtime-library/7461, which suggests that two passes might still be faster (growing an array with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See also https://discourse.julialang.org/t/half-vectorization/7399/3, which benchmarks some conditional comprehensions with somewhat alarming results. I think you really need to benchmark this change before we can decide. |
||
I = Vector{Int}(uninitialized, nnzA) | ||
cnt = 1 | ||
inds = _index_remapper(A) | ||
warned = false | ||
for (i,a) in enumerate(A) | ||
if !warned && !(a isa Bool) | ||
depwarn("In the future `find(A)` will only work on boolean collections. Use `find(x->x!=0, A)` instead.", :find) | ||
warned = true | ||
end | ||
if a != 0 | ||
I[cnt] = inds[i] | ||
cnt += 1 | ||
end | ||
if !(eltype(A) === Bool) && !all(x -> x isa Bool, A) | ||
depwarn("In the future `find(A)` will only work on boolean collections. Use `find(x->x!=0, A)` instead.", :find) | ||
end | ||
return I | ||
collect(first(p) for p in _pairs(A) if last(p) != 0) | ||
end | ||
|
||
find(x::Bool) = x ? [1] : Vector{Int}() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1274,7 +1274,7 @@ function find(p::Function, S::SparseMatrixCSC) | |
end | ||
sz = size(S) | ||
I, J = _findn(p, S) | ||
return Base._sub2ind(sz, I, J) | ||
return CartesianIndex.(I, J) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the new behavior of This method could also be optimized a bit by not allocating |
||
end | ||
find(p::Base.OccursIn, x::SparseMatrixCSC) = | ||
invoke(find, Tuple{Base.OccursIn, AbstractArray}, p, x) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you check the iterator trait here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? We don't guarantee that iterators implement
pairs
currently.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably use
countfrom(1)
.