diff --git a/NEWS.md b/NEWS.md index 785dc5be73f3e..ae4013cb098e6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -76,6 +76,7 @@ New library functions * New function `sincospi` for simultaneously computing `sinpi(x)` and `cospi(x)` more efficiently ([#35816]). * New function `addenv` for adding environment mappings into a `Cmd` object, returning the new `Cmd` object. +* New function `insorted` for determining whether an element is in a sorted collection or not ([#37490]). New library features -------------------- diff --git a/base/exports.jl b/base/exports.jl index ac00d7aa8e53a..2c0c628eec866 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -458,6 +458,7 @@ export searchsorted, searchsortedfirst, searchsortedlast, + insorted, startswith, # linear algebra diff --git a/base/operators.jl b/base/operators.jl index fc11b11ed5a32..060cb59c926d0 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -1095,7 +1095,8 @@ splat(f) = args->f(args...) in(x) Create a function that checks whether its argument is [`in`](@ref) `x`, i.e. -a function equivalent to `y -> y in x`. +a function equivalent to `y -> y in x`. See also [`insorted`](@ref) for the use +with sorted collections. The returned function is of type `Base.Fix2{typeof(in)}`, which can be used to implement specialized methods. diff --git a/base/sort.jl b/base/sort.jl index ddd09e89cc2ec..7771a24923825 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -27,6 +27,7 @@ export # also exported by Base searchsorted, searchsortedfirst, searchsortedlast, + insorted, # order & algorithm: sort, sort!, @@ -407,6 +408,39 @@ julia> searchsortedlast([1, 2, 4, 5, 5, 7], 0) # no match, insert at start ``` """ searchsortedlast +""" + insorted(a, x; by=, lt=, rev=false) + +Determine whether an item is in the given sorted collection, in the sense that +it is [`==`](@ref) to one of the values of the collection according to the order +specified by the `by`, `lt` and `rev` keywords, assuming that `a` is already +sorted in that order, see [`sort`](@ref) for the keywords. See also +[`in`](@ref). Returns a `Bool` value. + +# Examples +```jldoctest +julia> insorted(4, [1, 2, 4, 5, 5, 7]) # single match +true + +julia> insorted(5, [1, 2, 4, 5, 5, 7]) # multiple matches +true + +julia> insorted(3, [1, 2, 4, 5, 5, 7]) # no match +false + +julia> insorted(9, [1, 2, 4, 5, 5, 7]) # no match +false + +julia> insorted(0, [1, 2, 4, 5, 5, 7]) # no match +false +``` + +!!! compat "Julia 1.6" + `insorted` was added in Julia 1.6. +""" +function insorted end +insorted(x, v::AbstractVector; kw...) = !isempty(searchsorted(v, x; kw...)) +insorted(x, r::AbstractRange) = in(x, r) ## sorting algorithms ## diff --git a/doc/src/base/sort.md b/doc/src/base/sort.md index f4c07b1e138e8..ed6b5afab3e12 100644 --- a/doc/src/base/sort.md +++ b/doc/src/base/sort.md @@ -125,6 +125,7 @@ Base.issorted Base.Sort.searchsorted Base.Sort.searchsortedfirst Base.Sort.searchsortedlast +Base.Sort.insorted Base.Sort.partialsort! Base.Sort.partialsort Base.Sort.partialsortperm diff --git a/test/sorting.jl b/test/sorting.jl index ad5438514261e..776b2a574a57e 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -299,6 +299,61 @@ Base.step(r::ConstantRange) = 0 end end end +@testset "insorted" begin + numTypes = [Int8, Int16, Int32, Int64, Int128, + UInt8, UInt16, UInt32, UInt64, UInt128, + Float16, Float32, Float64, BigInt, BigFloat] + + @test insorted(1, collect(1:10), by=(>=(5))) + @test insorted(10, collect(1:10), by=(>=(5))) + + for R in numTypes, T in numTypes + @test !insorted(T(0), R[1, 1, 2, 2, 3, 3]) + @test insorted(T(1), R[1, 1, 2, 2, 3, 3]) + @test insorted(T(2), R[1, 1, 2, 2, 3, 3]) + @test !insorted(T(4), R[1, 1, 2, 2, 3, 3]) + @test !insorted(2.5, R[1, 1, 2, 2, 3, 3]) + + @test !insorted(T(0), 1:3) + @test insorted(T(1), 1:3) + @test insorted(T(2), 1:3) + @test !insorted(T(4), 1:3) + + @test insorted(T(1), R.(collect(1:10)), by=(>=(5))) + @test insorted(T(10), R.(collect(1:10)), by=(>=(5))) + end + + for (rg,I) in [(49:57,47:59), (1:2:17,-1:19), (-3:0.5:2,-5:.5:4)] + rg_r = reverse(rg) + rgv, rgv_r = collect(rg), collect(rg_r) + for i = I + @test insorted(i,rg) === insorted(i,rgv) + @test insorted(i,rg_r) === insorted(i,rgv_r,rev=true) + end + end + + rg = 0.0:0.01:1.0 + for i = 2:101 + @test insorted(rg[i], rg) + @test !insorted(prevfloat(rg[i]), rg) + @test !insorted(nextfloat(rg[i]), rg) + end + + rg_r = reverse(rg) + for i = 1:100 + @test insorted(rg_r[i], rg_r) + @test !insorted(prevfloat(rg_r[i]), rg_r) + @test !insorted(nextfloat(rg_r[i]), rg_r) + end + + @test insorted(1, 1:10) == insorted(1, collect(1:10), by=(>=(5))) + @test insorted(10, 1:10) == insorted(10, collect(1:10), by=(>=(5))) + + @test !insorted(0, []) + @test !insorted(0, [1,2,3]) + @test !insorted(4, [1,2,3]) + @test insorted(3, [10,8,6,9,4,7,2,5,3,1], by=(x -> iseven(x) ? x+5 : x), rev=true) +end @testset "PartialQuickSort" begin a = rand(1:10000, 1000) # test PartialQuickSort only does a partial sort