diff --git a/NEWS.md b/NEWS.md index f36e8bff553d0..3f9a7c582b7ef 100644 --- a/NEWS.md +++ b/NEWS.md @@ -47,6 +47,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]) * Two arguments method `lock(f, lck)` now accepts a `Channel` as the second argument. ([#39312]) * New functor `Returns(value)`, which returns `value` for any arguments ([#39794]) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 33c549d060273..c5dd8ae3660b4 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -2918,3 +2918,46 @@ 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` must be an iterator of sorted and unique 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) + local prev + i = firstindex(a) + for k in inds + 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 + end + prev = k + i = nextind(a, i) + end + deleteat!(a, i:lastindex(a)) + return a +end diff --git a/base/exports.jl b/base/exports.jl index 36a78c544acae..8b699e7e86af4 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -504,6 +504,7 @@ export count, delete!, deleteat!, + keepat!, eltype, empty!, empty, 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! diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 4e5e92881d601..9af20b8047701 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1364,3 +1364,24 @@ end @test [v v;;; fill(v, 1, 2)] == fill(v, 1, 2, 2) end 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