Non-mutating deleteat/splice function


#1

Is there a function to return a copy of an array with one or more elements removed? Or to splice in different elements into a copy of the array and return that?

I’ve been doing things like deleteat!(copy(arr), idxs), but since this is such a basic feature I wondered if I was missing an existing function to do it directly.


#2

getindex (aka []) and vcat should do what you want, but if you want to provide indexes in the format used by eg deleteat! you could use something like

julia> x = 1:5;

julia> x[setdiff(indices(x, 1), [2,3])]
3-element Array{Int64,1}:
 1
 4
 5

#3

The deleteat!(copy( solution actually seems faster than this method though.

julia> y = rand(1:100, 100);
julia> del_sd(a, idx) = a[setdiff(indices(a, 1), idx)];
julia> del_cp(a, idx) = deleteat!(copy(a), idx);
julia> @btime del_cp(y, (2, 3, 43))
  423.386 ns (1 allocation: 896 bytes)
97-element Array{Int64,1}:
 90
 88
 30
 41
 16
  ⋮
 22
 18
 78
 17

julia> @btime del_sd(y, (2, 3, 43))
  18.820 μs (25 allocations: 7.56 KiB)
97-element Array{Int64,1}:
 90
 88
 30
 41
 16
  ⋮
 22
 18
 78
 17

julia>

(AFAICT, a getindex+vcat type solution would get kinda unwieldly unless the indices to be removed are contiguous, and not as readable as the deleteat! version even for contiguous/single index cases.)

I’m ok with using the deleteat!(copy(arr), idxs) method, that just seemed a roundabout way to do this so I wondered if there was an easier more obvious way that I was missing.


#4

The logit of the _deleteat! makes some assumptions which make it very efficient, so it may be worthwhile converting it to a non-modifying version.


#5

What about

function deleteat(vec, idxs)
newlen = length(vec) - length(idxs)
vn = Vector{eltype(vec)}(newlen)
n=1
k=1
for idx in idxs
    while n < idx
        @inbounds vn[k]=vec[n]
        k+=1
        n+= 1
    end
    n+= 1
end
while k<=newlen
    @inbounds vn[k] = vec[n]
    k+=1
    n+=1
end
vn
end

Insert error checking (idxs must be sorted and unique) if you want. Roughly as fast as memcopy when deleting few elements, and significantly faster than taking a copy and then copying vast portions around again.

Luckily, in julia there is no reason to compose Base functions in weird ways when you can just write a straight loop :wink: