From 80c0bea1fcc881d015b9c08645190ed0752b4299 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 11 Jun 2020 13:11:45 +0200 Subject: [PATCH 1/6] add keepat! the opposite of deleteat! --- base/array.jl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/base/array.jl b/base/array.jl index c17d832d391f1..58a8dfbf40fdd 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1367,6 +1367,41 @@ function insert!(a::Array{T,1}, i::Integer, item) where T return a end +""" + keepat!(a::Vector, inds) + +Remove the items at all the indices which are not given by `inds`, and return the modified `a`. +Items which are kept are shifted to fill the resulting gaps. + +`inds` can be either an iterator or a collection of sorted and unique integer indices. +See also [`deleteat!`](@ref). + +# Examples +```jldoctest +julia> keepat!([6, 5, 4, 3, 2, 1], 1:2:5) +3-element Array{Int64,1}: + 6 + 4 + 2 +``` +""" +function keepat!(a::Vector, inds) + n = length(a) + l = 0 + i = 1 + for k in inds + 1 <= k <= n || throw(BoundsError(a, k)) + l < k || throw(ArgumentError("indices must be unique and sorted")) + if i != k + @inbounds a[i] = a[k] + end + l = k + i += 1 + end + _deleteend!(a, n-i+1) + a +end + """ deleteat!(a::Vector, i::Integer) From 792182baf209e5aff1ae7f13ed4615e2d40172db Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 22 Apr 2021 17:16:56 +0200 Subject: [PATCH 2/6] Update base/array.jl Co-authored-by: Jameson Nash --- base/array.jl | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/base/array.jl b/base/array.jl index 58a8dfbf40fdd..993f6b3f5ac99 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1385,21 +1385,20 @@ julia> keepat!([6, 5, 4, 3, 2, 1], 1:2:5) 2 ``` """ -function keepat!(a::Vector, inds) - n = length(a) - l = 0 - i = 1 +function keepat!(a::AbstractVector, inds) + isempty(inds) && return a + local prev + i = firstindex(a) for k in inds - 1 <= k <= n || throw(BoundsError(a, k)) - l < k || throw(ArgumentError("indices must be unique and sorted")) + @isdefined(prev) && (prev <= k || throw(ArgumentError("indices must be sorted"))) if i != k - @inbounds a[i] = a[k] + a[i] = a[k] end - l = k - i += 1 + prev = k + i = nextind(a, i) end - _deleteend!(a, n-i+1) - a + deleteat!(a, i:lastindex(a)) + return a end """ From e49e53698d430882464a31aabc2817f1ff1669b0 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 22 Apr 2021 17:25:26 +0200 Subject: [PATCH 3/6] add docs, compat, move to abstractarray.jl --- base/abstractarray.jl | 41 +++++++++++++++++++++++++++++++++++++ base/array.jl | 34 ------------------------------ doc/src/base/collections.md | 1 + 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index a5782b99e581c..dc5b828ff1140 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -2549,3 +2549,44 @@ function rest(a::AbstractArray{T}, state...) where {T} sizehint!(v, length(a)) return foldl(push!, Iterators.rest(a, state...), init=v) end + + +## keepat! ## + +""" + keepat!(a::AbstractVector, inds) + +Remove the items at all the indices which are not given by `inds`, +and return the modified `a`. +Items which are kept are shifted to fill the resulting gaps. + +`inds` can be either an iterator or a collection of sorted integer indices. +See also [`deleteat!`](@ref). + +!!! compat "Julia 1.7" + This function is available as of Julia 1.7. + +# Examples +```jldoctest +julia> keepat!([6, 5, 4, 3, 2, 1], 1:2:5) +3-element Array{Int64,1}: + 6 + 4 + 2 +``` +""" +function keepat!(a::AbstractVector, inds) + isempty(inds) && return a + local prev + i = firstindex(a) + for k in inds + @isdefined(prev) && (prev <= k || throw(ArgumentError("indices must be sorted"))) + if i != k + a[i] = a[k] + end + prev = k + i = nextind(a, i) + end + deleteat!(a, i:lastindex(a)) + return a +end diff --git a/base/array.jl b/base/array.jl index 993f6b3f5ac99..c17d832d391f1 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1367,40 +1367,6 @@ function insert!(a::Array{T,1}, i::Integer, item) where T return a end -""" - keepat!(a::Vector, inds) - -Remove the items at all the indices which are not given by `inds`, and return the modified `a`. -Items which are kept are shifted to fill the resulting gaps. - -`inds` can be either an iterator or a collection of sorted and unique integer indices. -See also [`deleteat!`](@ref). - -# Examples -```jldoctest -julia> keepat!([6, 5, 4, 3, 2, 1], 1:2:5) -3-element Array{Int64,1}: - 6 - 4 - 2 -``` -""" -function keepat!(a::AbstractVector, inds) - isempty(inds) && return a - local prev - i = firstindex(a) - for k in inds - @isdefined(prev) && (prev <= k || throw(ArgumentError("indices must be sorted"))) - if i != k - a[i] = a[k] - end - prev = k - i = nextind(a, i) - end - deleteat!(a, i:lastindex(a)) - return a -end - """ deleteat!(a::Vector, i::Integer) diff --git a/doc/src/base/collections.md b/doc/src/base/collections.md index fe4d8f80b1cd0..f8ef12071171a 100644 --- a/doc/src/base/collections.md +++ b/doc/src/base/collections.md @@ -273,6 +273,7 @@ Base.pushfirst! Base.popfirst! Base.insert! Base.deleteat! +Base.keepat! Base.splice! Base.resize! Base.append! From 322e9aa51bcb2903529c1e705cc6c914fd62b4d5 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 22 Apr 2021 17:50:21 +0200 Subject: [PATCH 4/6] NEWS, tests and some fixes --- NEWS.md | 2 ++ base/abstractarray.jl | 8 ++++---- test/abstractarray.jl | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index 24be1feb2108d..ad79b2b67c0d1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -39,6 +39,8 @@ New library functions * Two argument methods `findmax(f, domain)`, `argmax(f, domain)` and the corresponding `min` versions ([#27613]). * `isunordered(x)` returns true if `x` is value that is normally unordered, such as `NaN` or `missing`. +* New `keepat!(vector, inds)` function which is the inplace equivalent of `vector[inds]` + for a list `inds` of integers ([#36229]). * New macro `Base.@invokelatest f(args...; kwargs...)` provides a convenient way to call `Base.invokelatest(f, args...; kwargs...)` ([#37971]) * New macro `Base.@invoke f(arg1::T1, arg2::T2; kwargs...)` provides an easier syntax to call `invoke(f, Tuple{T1,T2}; kwargs...)` ([#38438]) * Two arguments method `lock(f, lck)` now accepts a `Channel` as the second argument. ([#39312]) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index dc5b828ff1140..192f9212dc809 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -2560,7 +2560,7 @@ Remove the items at all the indices which are not given by `inds`, and return the modified `a`. Items which are kept are shifted to fill the resulting gaps. -`inds` can be either an iterator or a collection of sorted integer indices. +`inds` must be an iterator of sorted and unique integer indices. See also [`deleteat!`](@ref). !!! compat "Julia 1.7" @@ -2576,13 +2576,13 @@ julia> keepat!([6, 5, 4, 3, 2, 1], 1:2:5) ``` """ function keepat!(a::AbstractVector, inds) - isempty(inds) && return a local prev i = firstindex(a) for k in inds - @isdefined(prev) && (prev <= k || throw(ArgumentError("indices must be sorted"))) + @isdefined(prev) && (prev < k || throw(ArgumentError("indices must be sorted"))) + ak = a[k] # must happen even when i==k for bounds checking if i != k - a[i] = a[k] + @inbounds a[i] = ak # k > i, so a[i] is inbounds end prev = k i = nextind(a, i) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index b75869f3ed9d6..28224ad21c045 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1298,3 +1298,24 @@ end @test @inferred(reduce(vcat, x_vecs)) == [5.0, 1.0, 2.0, 3.0] @test @inferred(reduce(vcat, ([10.0], [20.0], Bool[]))) == [10.0, 20.0] end + +@testset "keepat!" begin + a = [1:6;] + @test a === keepat!(a, 1:5) + @test a == 1:5 + @test keepat!(a, [2, 4]) == [2, 4] + @test isempty(keepat!(a, [])) + + a = [1:6;] + @test_throws BoundsError keepat!(a, 1:10) # make sure this is not a no-op + @test_throws BoundsError keepat!(a, 2:10) + @test_throws ArgumentError keepat!(a, [2, 4, 3]) + + b = BitVector([1, 1, 1, 0, 0]) + @test b === keepat!(b, 1:5) + @test b == [1, 1, 1, 0, 0] + @test keepat!(b, 2:4) == [1, 1, 0] + @test_throws BoundsError keepat!(a, -1:10) + @test_throws ArgumentError keepat!(a, [2, 1]) + @test isempty(keepat!(a, [])) +end From 130ad935d6d491afdb7786e84cba648370078f75 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 22 Apr 2021 18:01:07 +0200 Subject: [PATCH 5/6] update error message --- base/abstractarray.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 192f9212dc809..dc5261586a439 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -2579,7 +2579,9 @@ function keepat!(a::AbstractVector, inds) local prev i = firstindex(a) for k in inds - @isdefined(prev) && (prev < k || throw(ArgumentError("indices must be sorted"))) + if @isdefined(prev) + prev < k || throw(ArgumentError("indices must be unique and sorted")) + end ak = a[k] # must happen even when i==k for bounds checking if i != k @inbounds a[i] = ak # k > i, so a[i] is inbounds From c4a9b4318e21a737593a400844e13422acaebb4a Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 22 Apr 2021 18:20:41 +0200 Subject: [PATCH 6/6] export keepat --- base/exports.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/exports.jl b/base/exports.jl index 9a6cdce86b38c..ab524187cb34a 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -503,6 +503,7 @@ export count, delete!, deleteat!, + keepat!, eltype, empty!, empty,