From 8658ad2249cd39d32d80aa00d5279f0ed78cdc83 Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Sun, 14 Jan 2018 16:59:57 +0100 Subject: [PATCH 1/2] Change findfirst/findlast/findnext/findprev to return the same index type as keys() This is consistent with find(). We need to check whether the iterator is empty first, since calling first(keys(A)) may throw an error. Also add needed last(::EachStringIndex) and prevind(::AbstractArray, ::CartesianIndex) methods. --- NEWS.md | 12 +- base/array.jl | 227 +++++++++++++++++++++-------- base/deprecated.jl | 3 + base/multidimensional.jl | 6 +- base/strings/basic.jl | 2 + stdlib/SparseArrays/test/sparse.jl | 4 +- test/abstractarray.jl | 4 +- test/strings/basic.jl | 3 + 8 files changed, 189 insertions(+), 72 deletions(-) diff --git a/NEWS.md b/NEWS.md index 6f2abf7d786a1..f899efbd3e66d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -370,12 +370,12 @@ This section lists changes that do not have deprecation warnings. * `findn(x::AbstractArray)` has been deprecated in favor of `findall(!iszero, x)`, which now returns cartesian indices for multidimensional arrays (see below, [#25532]). - * `find` has been renamed to `findall`, and now returns the same type of indices - as `keys`/`pairs` for `AbstractArray`, `AbstractDict`, `AbstractString`, `Tuple` - and `NamedTuple` objects ([#24774], [#25545]). - In particular, this means that it returns `CartesianIndex` objects for matrices - and higher-dimensional arrays instead of linear indices as was previously the case. - Use `LinearIndices(a)[findall(f, a)]` to compute linear indices. + * `find` has been renamed to `findall`. `findall`, `findfirst`, `findlast`, `findnext` + now take and/or return the same type of indices as `keys`/`pairs` for `AbstractArray`, + `AbstractDict`, `AbstractString`, `Tuple` and `NamedTuple` objects ([#24774], [#25545]). + In particular, this means that they use `CartesianIndex` objects for matrices + and higher-dimensional arrays insted of linear indices as was previously the case. + Use `LinearIndices(a)[findall(f, a)]` and similar constructs to compute linear indices. * `AbstractSet` objects are now considered equal by `==` and `isequal` if all of their elements are equal ([#25368]). This has required changing the hashing algorithm diff --git a/base/array.jl b/base/array.jl index f5f779311a247..f64aec39f32a3 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1497,21 +1497,35 @@ cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(x->1, n-1)..., length(x Find the next linear index >= `i` of a `true` element of `A`, or `nothing` if not found. +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + # Examples ```jldoctest +julia> A = [false, false, true, false] +4-element Array{Bool,1}: + false + false + true + false + +julia> findnext(A, 1) +3 + +julia> findnext(A, 4) == nothing +true + julia> A = [false false; true false] 2×2 Array{Bool,2}: false false true false -julia> findnext(A, 1) -2 - -julia> findnext(A, 3) +julia> findnext(A, CartesianIndex(1, 1)) +CartesianIndex(2, 1) ``` """ -function findnext(A, start::Integer) - l = endof(A) +function findnext(A, start) + l = last(keys(A)) i = start warned = false while i <= l @@ -1531,48 +1545,74 @@ end """ findfirst(A) -Return the linear index of the first `true` value in `A`. +Return the index of the first `true` value in `A`. Return `nothing` if no such value is found. To search for other kinds of values, pass a predicate as the first argument. +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + # Examples ```jldoctest +julia> A = [false, false, true, false] +4-element Array{Bool,1}: + false + false + true + false + +julia> findfirst(A) +3 + +julia> findfirst(falses(3)) == nothing +true + julia> A = [false false; true false] 2×2 Array{Bool,2}: false false true false julia> findfirst(A) -2 - -julia> findfirst(falses(3)) == nothing -true +CartesianIndex(2, 1) ``` """ -findfirst(A) = findnext(A, 1) +findfirst(A) = isempty(A) ? nothing : findnext(A, first(keys(A))) """ - findnext(predicate::Function, A, i::Integer) + findnext(predicate::Function, A, i) -Find the next linear index >= `i` of an element of `A` for which `predicate` returns `true`, +Find the next index >= `i` of an element of `A` for which `predicate` returns `true`, or `nothing` if not found. +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + # Examples ```jldoctest -julia> A = [1 4; 2 2] -2×2 Array{Int64,2}: - 1 4 - 2 2 +A = [1, 4, 2, 2] +4-element Array{Int64,1}: + 1 + 4 + 2 + 2 julia> findnext(isodd, A, 1) 1 julia> findnext(isodd, A, 2) == nothing true + +julia> A = [1 4; 2 2] +2×2 Array{Int64,2}: + 1 4 + 2 2 + +julia> findnext(isodd, A, CartesianIndex(1, 1)) +CartesianIndex(1, 1) ``` """ -function findnext(testf::Function, A, start::Integer) - l = endof(A) +function findnext(testf::Function, A, start) + l = last(keys(A)) i = start while i <= l if testf(A[i]) @@ -1586,15 +1626,20 @@ end """ findfirst(predicate::Function, A) -Return the linear index of the first element of `A` for which `predicate` returns `true`. +Return the index of the first element of `A` for which `predicate` returns `true`. Return `nothing` if there is no such element. +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + # Examples ```jldoctest -julia> A = [1 4; 2 2] -2×2 Array{Int64,2}: - 1 4 - 2 2 +julia> A = [1, 4, 2, 2] +4-element Array{Int64,1}: + 1 + 4 + 2 + 2 julia> findfirst(iseven, A) 2 @@ -1603,34 +1648,55 @@ julia> findfirst(x -> x>10, A) == nothing true julia> findfirst(equalto(4), A) -3 +2 + +julia> A = [1 4; 2 2] +2×2 Array{Int64,2}: + 1 4 + 2 2 + +julia> findfirst(iseven, A) +CartesianIndex(2, 1) ``` """ -findfirst(testf::Function, A) = findnext(testf, A, 1) +findfirst(testf::Function, A) = isempty(A) ? nothing : findnext(testf, A, first(keys(A))) """ - findprev(A, i::Integer) + findprev(A, i) + +Find the previous index <= `i` of a `true` element of `A`, or `nothing` if not found. -Find the previous linear index <= `i` of a `true` element of `A`, or `nothing` if not found. +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). # Examples ```jldoctest +julia> A = [false, false, true, true] +4-element Array{Bool,1}: + false + false + true + true + +julia> findprev(A, 3) +3 + +julia> findprev(A, 1) == nothing +true + julia> A = [false false; true true] 2×2 Array{Bool,2}: false false true true -julia> findprev(A,2) -2 - -julia> findprev(A,1) == nothing -true +julia> findprev(A, CartesianIndex(2, 1)) +CartesianIndex(2, 1) ``` """ -function findprev(A, start::Integer) +function findprev(A, start) i = start warned = false - while i >= 1 + while i >= first(keys(A)) a = A[i] if !warned && !(a isa Bool) depwarn("In the future `findprev` will only work on boolean collections. Use `findprev(x->x!=0, A, start)` instead.", :findprev) @@ -1645,50 +1711,76 @@ end """ findlast(A) -Return the linear index of the last `true` value in `A`. +Return the index of the last `true` value in `A`. Return `nothing` if there is no `true` value in `A`. +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + # Examples ```jldoctest -julia> A = [true false; true false] -2×2 Array{Bool,2}: - true false - true false +julia> A = [true, false, true, false] +4-element Array{Bool,1}: + true + false + true + false julia> findlast(A) -2 +3 julia> A = falses(2,2); julia> findlast(A) == nothing true + +julia> A = [true false; true false] +2×2 Array{Bool,2}: + true false + true false + +julia> findlast(A) +CartesianIndex(2, 1) ``` """ -findlast(A) = findprev(A, endof(A)) +findlast(A) = isempty(A) ? nothing : findprev(A, last(keys(A))) """ - findprev(predicate::Function, A, i::Integer) + findprev(predicate::Function, A, i) -Find the previous linear index <= `i` of an element of `A` for which `predicate` returns `true`, or +Find the previous index <= `i` of an element of `A` for which `predicate` returns `true`, or `nothing` if not found. +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + # Examples ```jldoctest -julia> A = [4 6; 1 2] -2×2 Array{Int64,2}: - 4 6 - 1 2 +julia> A = [4, 6, 1, 2] +4-element Array{Int64,1}: + 4 + 6 + 1 + 2 julia> findprev(isodd, A, 1) == nothing true julia> findprev(isodd, A, 3) -2 +3 + +julia> A = [4 6; 1 2] +2×2 Array{Int64,2}: + 4 6 + 1 2 + +julia> findprev(isodd, A, CartesianIndex(1, 2)) +CartesianIndex(2, 1) ``` """ -function findprev(testf::Function, A, start::Integer) +function findprev(testf::Function, A, start) i = start - while i >= 1 + while i >= first(keys(A)) testf(A[i]) && return i i = prevind(A, i) end @@ -1698,24 +1790,37 @@ end """ findlast(predicate::Function, A) -Return the linear index of the last element of `A` for which `predicate` returns `true`. +Return the index of the last element of `A` for which `predicate` returns `true`. Return `nothing` if there is no such element. +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + # Examples ```jldoctest +julia> A = [1, 2, 3, 4] +4-element Array{Int64,1}: + 1 + 2 + 3 + 4 + +julia> findlast(isodd, A) +3 + +julia> findlast(x -> x > 5, A) == nothing +true + julia> A = [1 2; 3 4] 2×2 Array{Int64,2}: 1 2 3 4 julia> findlast(isodd, A) -2 - -julia> findlast(x -> x > 5, A) == nothing -true +CartesianIndex(2, 1) ``` """ -findlast(testf::Function, A) = findprev(testf, A, endof(A)) +findlast(testf::Function, A) = isempty(A) ? nothing : findprev(testf, A, last(keys(A))) """ findall(f::Function, A) @@ -1723,10 +1828,8 @@ findlast(testf::Function, A) = findprev(testf, A, endof(A)) 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`, `AbstractDict`, `AbstractString` -`Tuple` and `NamedTuple` objects, and are linear indices starting at `1` -for other iterables. +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). # Examples ```jldoctest diff --git a/base/deprecated.jl b/base/deprecated.jl index bc7776350873c..a65148a1b162a 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1184,6 +1184,9 @@ end @deprecate findfirst(A, v) findfirst(equalto(v), A) @deprecate findprev(A, v, i::Integer) findprev(equalto(v), A, i) @deprecate findlast(A, v) findlast(equalto(v), A) +# to fix ambiguities introduced by deprecations +findnext(pred::Function, A, i::Integer) = invoke(findnext, Tuple{Function, Any, Any}, pred, A, i) +findprev(pred::Function, A, i::Integer) = invoke(findprev, Tuple{Function, Any, Any}, pred, A, i) # also remove deprecation warnings in find* functions in array.jl, sparse/sparsematrix.jl, # and sparse/sparsevector.jl. diff --git a/base/multidimensional.jl b/base/multidimensional.jl index d49aa104e21a4..555c9d64695c0 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -142,11 +142,15 @@ module IteratorsMD return h end - # nextind with CartesianIndex + # nextind and prevind with CartesianIndex function Base.nextind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N} _, ni = next(CartesianIndices(axes(a)), i) return ni end + function Base.prevind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N} + _, ni = next(Iterators.reverse(CartesianIndices(axes(a))), i) + return ni + end # Iteration over the elements of CartesianIndex cannot be supported until its length can be inferred, # see #23719 diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 5b9f00874a5a3..8ed6dab404919 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -480,6 +480,8 @@ end keys(s::AbstractString) = EachStringIndex(s) length(e::EachStringIndex) = length(e.s) +first(::EachStringIndex) = 1 +last(e::EachStringIndex) = endof(e.s) start(e::EachStringIndex) = start(e.s) next(e::EachStringIndex, state) = (state, nextind(e.s, state)) done(e::EachStringIndex, state) = done(e.s, state) diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index 299b7dfe85e94..446628cb5d0ed 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -2183,7 +2183,7 @@ end 1 0 1 1 0] y_sp = sparse(y) - for i=1:length(y) + for i in keys(y) @test findnext(!iszero, y,i) == findnext(!iszero, y_sp,i) @test findprev(!iszero, y,i) == findprev(!iszero, y_sp,i) end @@ -2191,7 +2191,7 @@ end z_sp = sparsevec(Dict(1=>1, 5=>1, 8=>0, 10=>1)) z = collect(z_sp) - for i=1:length(z) + for i in keys(z) @test findnext(!iszero, z,i) == findnext(!iszero, z_sp,i) @test findprev(!iszero, z,i) == findprev(!iszero, z_sp,i) end diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 820c2ae81ad5d..24416c9245bed 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -817,9 +817,11 @@ for A in (rand(2), rand(2,3)) @test Array(values(A)) == A end -# nextind +# nextind and prevind @test nextind(zeros(4), 2) == 3 @test nextind(zeros(2,3), CartesianIndex(2,1)) == CartesianIndex(1, 2) +@test prevind(zeros(4), 2) == 1 +@test prevind(zeros(2,3), CartesianIndex(2,1)) == CartesianIndex(1, 1) @testset "ImageCore #40" begin Base.convert(::Type{Array{T,n}}, a::Array{T,n}) where {T<:Number,n} = a diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 29ea38b3664f2..dcf007e6860ab 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -233,6 +233,9 @@ end @test_throws StringIndexError GenericString("∀∃")[Int8(2)] @test_throws BoundsError GenericString("∀∃")[UInt16(10)] + @test first(eachindex("foobar")) === 1 + @test first(eachindex("")) === 1 + @test last(eachindex("foobar")) === endof("foobar") @test done(eachindex("foobar"),7) @test eltype(Base.EachStringIndex) == Int @test map(uppercase, "foó") == "FOÓ" From 80e4d44901aa010475195586d5cca1303220915c Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Tue, 16 Jan 2018 10:13:14 +0100 Subject: [PATCH 2/2] Use more generic approach for findfirst and findlast Also add tests, and remove @inferred calls which did not actually check whether functions were inferred, but only whether the equality test was. Change the _pairs() fallback to use Iterators.countfrom() so that an error is raised by findlast() rather than assuming the last index is typemax(Int), and use length() for iterators which support it so that findlast() works. --- base/array.jl | 102 +++++++++++++++++++++++++++++++++++---------- test/arrayops.jl | 41 +++++++++++++++--- test/dict.jl | 15 ++++--- test/namedtuple.jl | 16 +++++-- test/tuple.jl | 26 +++++++++--- 5 files changed, 160 insertions(+), 40 deletions(-) diff --git a/base/array.jl b/base/array.jl index f64aec39f32a3..f621bc43ed224 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1492,6 +1492,11 @@ cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(x->1, n-1)..., length(x ## find ## +_pairs(A::Union{AbstractArray, AbstractDict, AbstractString, Tuple, NamedTuple}) = pairs(A) +_pairs(iter) = _pairs(IteratorSize(iter), iter) +_pairs(::Union{HasLength, HasShape}, iter) = zip(1:length(iter), iter) +_pairs(::Union{SizeUnknown, IsInfinite}, iter) = zip(Iterators.countfrom(1), iter) + """ findnext(A, i::Integer) @@ -1545,12 +1550,14 @@ end """ findfirst(A) -Return the index of the first `true` value in `A`. +Return the index or key of the first `true` value in `A`. Return `nothing` if no such value is found. To search for other kinds of values, pass a predicate as the first argument. -Indices are of the same type as those returned by [`keys(A)`](@ref) -and [`pairs(A)`](@ref). +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref) for `AbstractArray`, `AbstractDict`, `AbstractString` +`Tuple` and `NamedTuple` objects, and are linear indices starting at `1` +for other iterables. # Examples ```jldoctest @@ -1576,7 +1583,22 @@ julia> findfirst(A) CartesianIndex(2, 1) ``` """ -findfirst(A) = isempty(A) ? nothing : findnext(A, first(keys(A))) +function findfirst(A) + warned = false + for (i, a) in _pairs(A) + if !warned && !(a isa Bool) + depwarn("In the future `findfirst` will only work on boolean collections. Use `findfirst(x->x!=0, A)` instead.", :findfirst) + warned = true + end + if a != 0 + return i + end + end + return nothing +end + +# Needed for bootstrap, and allows defining only an optimized findnext method +findfirst(A::Union{AbstractArray, AbstractString}) = findnext(A, first(keys(A))) """ findnext(predicate::Function, A, i) @@ -1626,11 +1648,13 @@ end """ findfirst(predicate::Function, A) -Return the index of the first element of `A` for which `predicate` returns `true`. +Return the index or key of the first element of `A` for which `predicate` returns `true`. Return `nothing` if there is no such element. -Indices are of the same type as those returned by [`keys(A)`](@ref) -and [`pairs(A)`](@ref). +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref) for `AbstractArray`, `AbstractDict`, `AbstractString` +`Tuple` and `NamedTuple` objects, and are linear indices starting at `1` +for other iterables. # Examples ```jldoctest @@ -1659,7 +1683,16 @@ julia> findfirst(iseven, A) CartesianIndex(2, 1) ``` """ -findfirst(testf::Function, A) = isempty(A) ? nothing : findnext(testf, A, first(keys(A))) +function findfirst(testf::Function, A) + for (i, a) in _pairs(A) + testf(a) && return i + end + return nothing +end + +# Needed for bootstrap, and allows defining only an optimized findnext method +findfirst(testf::Function, A::Union{AbstractArray, AbstractString}) = + findnext(testf, A, first(keys(A))) """ findprev(A, i) @@ -1711,11 +1744,13 @@ end """ findlast(A) -Return the index of the last `true` value in `A`. +Return the index or key of the last `true` value in `A`. Return `nothing` if there is no `true` value in `A`. -Indices are of the same type as those returned by [`keys(A)`](@ref) -and [`pairs(A)`](@ref). +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref) for `AbstractArray`, `AbstractDict`, `AbstractString` +`Tuple` and `NamedTuple` objects, and are linear indices starting at `1` +for other iterables. # Examples ```jldoctest @@ -1743,7 +1778,22 @@ julia> findlast(A) CartesianIndex(2, 1) ``` """ -findlast(A) = isempty(A) ? nothing : findprev(A, last(keys(A))) +function findlast(A) + warned = false + for (i, a) in Iterators.reverse(_pairs(A)) + if !warned && !(a isa Bool) + depwarn("In the future `findlast` will only work on boolean collections. Use `findlast(x->x!=0, A)` instead.", :findlast) + warned = true + end + if a != 0 + return i + end + end + return nothing +end + +# Needed for bootstrap, and allows defining only an optimized findprev method +findlast(A::Union{AbstractArray, AbstractString}) = findprev(A, last(keys(A))) """ findprev(predicate::Function, A, i) @@ -1790,11 +1840,13 @@ end """ findlast(predicate::Function, A) -Return the index of the last element of `A` for which `predicate` returns `true`. +Return the index or key of the last element of `A` for which `predicate` returns `true`. Return `nothing` if there is no such element. -Indices are of the same type as those returned by [`keys(A)`](@ref) -and [`pairs(A)`](@ref). +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref) for `AbstractArray`, `AbstractDict`, `AbstractString` +`Tuple` and `NamedTuple` objects, and are linear indices starting at `1` +for other iterables. # Examples ```jldoctest @@ -1820,7 +1872,16 @@ julia> findlast(isodd, A) CartesianIndex(2, 1) ``` """ -findlast(testf::Function, A) = isempty(A) ? nothing : findprev(testf, A, last(keys(A))) +function findlast(testf::Function, A) + for (i, a) in Iterators.reverse(_pairs(A)) + testf(a) && return i + end + return nothing +end + +# Needed for bootstrap, and allows defining only an optimized findprev method +findlast(testf::Function, A::Union{AbstractArray, AbstractString}) = + findprev(testf, A, last(keys(A))) """ findall(f::Function, A) @@ -1828,8 +1889,10 @@ findlast(testf::Function, A) = isempty(A) ? nothing : findprev(testf, A, last(ke 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 are of the same type as those returned by [`keys(A)`](@ref) -and [`pairs(A)`](@ref). +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref) for `AbstractArray`, `AbstractDict`, `AbstractString` +`Tuple` and `NamedTuple` objects, and are linear indices starting at `1` +for other iterables. # Examples ```jldoctest @@ -1875,9 +1938,6 @@ julia> findall(x -> x >= 0, d) """ findall(testf::Function, A) = collect(first(p) for p in _pairs(A) if testf(last(p))) -_pairs(A::Union{AbstractArray, AbstractDict, AbstractString, Tuple, NamedTuple}) = pairs(A) -_pairs(iter) = zip(OneTo(typemax(Int)), iter) # safe for objects that don't implement length - """ findall(A) diff --git a/test/arrayops.jl b/test/arrayops.jl index 7157ca70e3360..ce1e3a7082894 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -460,18 +460,49 @@ end @test findnext(equalto(0x00), [0x00, 0x01, 0x00], 2) == 3 @test findprev(equalto(0x00), [0x00, 0x01, 0x00], 2) == 1 end -@testset "findall with Matrix" begin +@testset "find with Matrix" begin A = [1 2 0; 3 4 0] @test findall(isodd, A) == [CartesianIndex(1, 1), CartesianIndex(2, 1)] @test findall(!iszero, A) == [CartesianIndex(1, 1), CartesianIndex(2, 1), CartesianIndex(1, 2), CartesianIndex(2, 2)] -end -@testset "findall with general iterables" begin + @test findfirst(isodd, A) == CartesianIndex(1, 1) + @test findlast(isodd, A) == CartesianIndex(2, 1) + @test findnext(isodd, A, CartesianIndex(1, 1)) == CartesianIndex(1, 1) + @test findprev(isodd, A, CartesianIndex(2, 1)) == CartesianIndex(2, 1) + @test findnext(isodd, A, CartesianIndex(1, 2)) === nothing + @test findprev(iseven, A, CartesianIndex(2, 1)) === nothing +end +@testset "find with general iterables" begin s = "julia" @test findall(c -> c == 'l', s) == [3] + @test findfirst(c -> c == 'l', s) == 3 + @test findlast(c -> c == 'l', s) == 3 + @test findnext(c -> c == 'l', s, 1) == 3 + @test findprev(c -> c == 'l', s, 5) == 3 + @test findnext(c -> c == 'l', s, 4) === nothing + @test findprev(c -> c == 'l', s, 2) === nothing + g = Base.Unicode.graphemes("日本語") - @test findall(isascii, g) == Int[] - @test findall(!iszero, (i % 2 for i in 1:10)) == 1:2:9 + @test findall(!isempty, g) == 1:3 + @test isempty(findall(isascii, g)) + @test findfirst(!isempty, g) == 1 + @test findfirst(isascii, g) === nothing + # Check that the last index isn't assumed to be typemax(Int) + @test_throws MethodError findlast(!iszero, g) + + g2 = (i % 2 for i in 1:10) + @test findall(!iszero, g2) == 1:2:9 + @test findfirst(!iszero, g2) == 1 + @test findlast(!iszero, g2) == 9 + @test findfirst(equalto(2), g2) === nothing + @test findlast(equalto(2), g2) === nothing + + g3 = (i % 2 for i in 1:10, j in 1:2) + @test findall(!iszero, g3) == 1:2:19 + @test findfirst(!iszero, g3) == 1 + @test findlast(!iszero, g3) == 19 + @test findfirst(equalto(2), g3) === nothing + @test findlast(equalto(2), g3) === nothing end @testset "findmin findmax indmin indmax" begin diff --git a/test/dict.jl b/test/dict.jl index 45d60f6210eb5..5b9c3da1935ba 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -760,9 +760,14 @@ end @test map(string, keys(d)) == Set(["1","3"]) end -@testset "findall" begin - @test @inferred findall(equalto(1), Dict(:a=>1, :b=>2)) == [:a] - @test @inferred sort(findall(equalto(1), Dict(:a=>1, :b=>1))) == [:a, :b] - @test @inferred isempty(findall(equalto(1), Dict())) - @test @inferred isempty(findall(equalto(1), Dict(:a=>2, :b=>3))) +@testset "find" begin + @test findall(equalto(1), Dict(:a=>1, :b=>2)) == [:a] + @test sort(findall(equalto(1), Dict(:a=>1, :b=>1))) == [:a, :b] + @test isempty(findall(equalto(1), Dict())) + @test isempty(findall(equalto(1), Dict(:a=>2, :b=>3))) + + @test findfirst(equalto(1), Dict(:a=>1, :b=>2)) == :a + @test findfirst(equalto(1), Dict(:a=>1, :b=>1, :c=>3)) in (:a, :b) + @test findfirst(equalto(1), Dict()) === nothing + @test findfirst(equalto(1), Dict(:a=>2, :b=>3)) === nothing end \ No newline at end of file diff --git a/test/namedtuple.jl b/test/namedtuple.jl index c20bc717f4615..85d8bd1816e15 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -209,7 +209,15 @@ abstr_nt_22194_3() @test typeof(Base.structdiff(NamedTuple{(:a, :b), Tuple{Int32, Union{Int32, Nothing}}}((1, Int32(2))), (a=0,))) === NamedTuple{(:b,), Tuple{Union{Int32, Nothing}}} -@test @inferred findall(equalto(1), (a=1, b=2)) == [:a] -@test @inferred findall(equalto(1), (a=1, b=1)) == [:a, :b] -@test @inferred isempty(findall(equalto(1), NamedTuple())) -@test @inferred isempty(findall(equalto(1), (a=2, b=3))) \ No newline at end of file +@test findall(equalto(1), (a=1, b=2)) == [:a] +@test findall(equalto(1), (a=1, b=1)) == [:a, :b] +@test isempty(findall(equalto(1), NamedTuple())) +@test isempty(findall(equalto(1), (a=2, b=3))) +@test findfirst(equalto(1), (a=1, b=2)) == :a +@test findlast(equalto(1), (a=1, b=2)) == :a +@test findfirst(equalto(1), (a=1, b=1)) == :a +@test findlast(equalto(1), (a=1, b=1)) == :b +@test findfirst(equalto(1), ()) === nothing +@test findlast(equalto(1), ()) === nothing +@test findfirst(equalto(1), (a=2, b=3)) === nothing +@test findlast(equalto(1), (a=2, b=3)) === nothing diff --git a/test/tuple.jl b/test/tuple.jl index 0e74b267b5b85..713592d2e1980 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -365,9 +365,25 @@ end @test eltype(Tuple{Vararg{T}} where T<:Integer) >: Integer end -@testset "findall" begin - @test @inferred findall(equalto(1), (1, 2)) == [1] - @test @inferred findall(equalto(1), (1, 1)) == [1, 2] - @test @inferred isempty(findall(equalto(1), ())) - @test @inferred isempty(findall(equalto(1), (2, 3))) +@testset "find" begin + @test findall(equalto(1), (1, 2)) == [1] + @test findall(equalto(1), (1, 1)) == [1, 2] + @test isempty(findall(equalto(1), ())) + @test isempty(findall(equalto(1), (2, 3))) + + @test findfirst(equalto(1), (1, 2)) == 1 + @test findlast(equalto(1), (1, 2)) == 1 + @test findfirst(equalto(1), (1, 1)) == 1 + @test findlast(equalto(1), (1, 1)) == 2 + @test findfirst(equalto(1), ()) === nothing + @test findlast(equalto(1), ()) === nothing + @test findfirst(equalto(1), (2, 3)) === nothing + @test findlast(equalto(1), (2, 3)) === nothing + + @test findnext(equalto(1), (1, 2), 1) == 1 + @test findprev(equalto(1), (1, 2), 2) == 1 + @test findnext(equalto(1), (1, 1), 2) == 2 + @test findprev(equalto(1), (1, 1), 1) == 1 + @test findnext(equalto(1), (2, 3), 1) === nothing + @test findprev(equalto(1), (2, 3), 2) === nothing end \ No newline at end of file