# Elegant way of exclusive indexing

**URL:** https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894
**Category:** General Usage
**Tags:** indexing, arrays
**Created:** [February 22, 2022, 9:24am UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894 "2022-02-22T09:24:31Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![TheLateKronos](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thelatekronos/32/12824_2.png) [@TheLateKronos](https://discourse.julialang.org/u/TheLateKronos)
#### Post date: [February 22, 2022, 9:24am UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/1 "2022-02-22T09:24:32Z")

</div>

In R, you can index all elements _except_ for the second, by negative indexing: `myvector[-2]`. This is especially usefull if you want to make two complete subsets of a vector:

```julia
set1 = myvector[inds]
set2 = myvector[-inds]

```

What do you find to be the best way of implementing “negative indexing”?

I’ll start with my current inelegant solution:

```julia
julia> myvector = 1:10|>vec
1:10

julia> inds = [2, 5, 6]
3-element Vector{Int64}:
 2
 5
 6

julia> set1 = myvector[inds]
3-element Vector{Int64}:
 2
 5
 6

julia> set2 = myvector[[i ∉ inds for i in eachindex(myvector)]]
7-element Vector{Int64}:
  1
  3
  4
  7
  8
  9
 10

```

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 22, 2022, 9:28am UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/2 "2022-02-22T09:28:29Z")

</div>

[https://github.com/JuliaData/InvertedIndices.jl](https://github.com/JuliaData/InvertedIndices.jl)

---

<div class="post-metadata">

### Author: ![fredrikekre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fredrikekre/32/1688_2.png) [@fredrikekre](https://discourse.julialang.org/u/fredrikekre)
#### Post date: [February 22, 2022, 9:28am UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/3 "2022-02-22T09:28:41Z")

</div>

You can use the [InvertedIndices.jl](https://github.com/JuliaData/InvertedIndices.jl) package:

```julia
julia> using InvertedIndices

julia> v = 1:10;

julia> inds = [2, 5, 6];

julia> v[inds]
3-element Vector{Int64}:
 2
 5
 6

julia> v[Not(inds)]
7-element Vector{Int64}:
  1
  3
  4
  7
  8
  9
 10

```

---

<div class="post-metadata">

### Author: ![TheLateKronos](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thelatekronos/32/12824_2.png) [@TheLateKronos](https://discourse.julialang.org/u/TheLateKronos)
#### Post date: [February 22, 2022, 9:35am UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/4 "2022-02-22T09:35:00Z")

</div>

Thanks for the reference! It is nice that this problem is already solved by a package. However, I feel that adding a package dependency seems a bit much for such a simple task, especially if it is only needed a single time.

The following example uses the `Base` function `setdiff`, which I found to be an improvement on my initial attempt:

```julia
julia> set2 = myvector[setdiff(eachindex(myvector), inds)]
7-element Vector{Int64}:
  1
  3
  4
  7
  8
  9
 10

```

---

<div class="post-metadata">

### Author: ![TheLateKronos](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thelatekronos/32/12824_2.png) [@TheLateKronos](https://discourse.julialang.org/u/TheLateKronos)
#### Post date: [February 22, 2022, 9:41am UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/5 "2022-02-22T09:41:23Z")

</div>

You can use elementwise `∉` if you wrap the second collection in a single element iterable:

```julia
julia> set2 = myvector[eachindex(myvector) .∉ [inds]]
7-element Vector{Int64}:
  1
  3
  4
  7
  8
  9
 10

julia> set2 = myvector[eachindex(myvector) .∉ (inds,)]
7-element Vector{Int64}:
  1
  3
  4
  7
  8
  9
 10

```

---

<div class="post-metadata">

### Author: ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)
#### Post date: [February 22, 2022, 12:15pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/6 "2022-02-22T12:15:52Z")

</div>

a slight refinement

```julia
v[∉(inds).(eachindex(v))]
v[∉(inds).(1:end)]

```

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [February 22, 2022, 12:27pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/7 "2022-02-22T12:27:19Z")

</div>

If you want to iterate (quickly) on these elements, without allocating an intermediate array, you can use `Iterators.filter`:

```julia
julia> x = collect(1:10);

julia> inds = [2,5,6];

julia> for i in Iterators.filter(!in(inds),eachindex(x))
           @show x[i]
       end
x[i] = 1
x[i] = 3
x[i] = 4
x[i] = 7
x[i] = 8
x[i] = 9
x[i] = 10

```

---

<div class="post-metadata">

### Author: ![rfourquet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rfourquet/32/3610_2.png) [@rfourquet](https://discourse.julialang.org/u/rfourquet)
#### Post date: [February 22, 2022, 12:30pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/8 "2022-02-22T12:30:03Z")

</div>

There is also: `deleteat!(collect(v), inds)` (or `copy` instead of `collect` if `v` is already a `Vector`).

---

<div class="post-metadata">

### Author: ![jonniedie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonniedie/32/12842_2.png) [@jonniedie](https://discourse.julialang.org/u/jonniedie)
#### Post date: [February 22, 2022, 2:35pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/9 "2022-02-22T14:35:05Z")

</div>

> [@TheLateKronos](#):
>
> Thanks for the reference! It is nice that this problem is already solved by a package. However, I feel that adding a package dependency seems a bit much for such a simple task, especially if it is only needed a single time.

For what it’s worth, InvertedIndices.jl only defines and exports the `InvertedIndex` type (also aliased as `Not`) and itself has no dependencies. I generally consider small self-contained packages like this as not actually being dependencies because they don’t really slow down precompilation or importing and there is no chance that they will break compatibility with other packages.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 22, 2022, 3:19pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/10 "2022-02-22T15:19:58Z")

</div>

The `InvertedIndices.jl` solution seems to allocate a lot and to be much slower than the other solutions posted.

The `deleteat!()` is the fastest but requires the indices to be sorted.

---

<div class="post-metadata">

### Author: ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)
#### Post date: [February 22, 2022, 5:08pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/11 "2022-02-22T17:08:48Z")

</div>

> [@jonniedie](#):
>
> I generally consider small self-contained packages like this as not actually being dependencies because they don’t really slow down precompilation or importing and there is no chance that they will break compatibility with other packages.

Well it’s technically a (fast to load) dependency:

```julia
julia> @time using InvertedIndices
  0.019317 seconds (5.43 k allocations: 422.184 KiB, 54.67% compilation time)

```

While needing it may not be too common, I wander if it or something similar should be a Julia stdlib. Mostly for discovery. `and`, `or` and I think `not` have been suggested for Julia 2.0 (as aliases, or by me with slightly different semantics).

I’m not sure upper-cased Not is already claimed by some other package, also seems like a bad variable name…

If this where to be merged into Julia, then better sooner rather than later? The API could be the same, but clearly the speed could be improved. If not considered, put a link to it in the official docs, mention in

[https://docs.julialang.org/en/v1/manual/noteworthy-differences/#Noteworthy-differences-from-R](https://docs.julialang.org/en/v1/manual/noteworthy-differences/#Noteworthy-differences-from-R)

Just curious, how would you do the same in Python?

---

<div class="post-metadata">

### Author: ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)
#### Post date: [February 22, 2022, 5:11pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/12 "2022-02-22T17:11:04Z")

</div>

in the following function I tried to make the most of the info that inds is sorted.  
Apart from the use of specific information what else can be done to improve the execution time?

```julia

function dat(v,idx)
mid=Int[]
pre=@view v[1:inds[1]-1]
post=@view v[inds[end]+1:end]
shinds=inds[2:end-1] .-inds[1]
@inbounds for (i,e) in enumerate(v[inds[1]+1:inds[end]-1])
        if i ∉ shinds
            push!(mid,e)
        end
    end
[pre; mid; post]
end

```

---

<div class="post-metadata">

### Author: ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)
#### Post date: [February 22, 2022, 5:16pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/13 "2022-02-22T17:16:12Z")

</div>

A performant and really flexible solution is to use `Accessors.jl`: `@delete A[123]` returns a copy of `A` without element at index `123`.  
It has the same performance as manual selection:

```julia
julia> A = rand(1000);

# manual
julia> @btime $A[[1:122; 124:end]];
  1.270 μs (2 allocations: 15.88 KiB)

# Accessors.jl
julia> @btime @delete A[123];
  1.377 μs (3 allocations: 16.00 KiB)

# InvertedIndices.jl: 100x slower!
julia> @btime $A[Not(123)];
  165.270 μs (6097 allocations: 183.12 KiB)

```

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 22, 2022, 5:21pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/14 "2022-02-22T17:21:47Z")

</div>

@aplavin, how do we apply `@delete` to an array of indices as in OP?

---

<div class="post-metadata">

### Author: ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)
#### Post date: [February 22, 2022, 5:26pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/15 "2022-02-22T17:26:30Z")

</div>

Sorry, I didn’t read in enough details first, and only noticed the `myvector[-2]` example with a single index. Indeed, `@delete` only works for a single index now, saying as an author of its implementation (: Basically, I only needed single indices myself, and didn’t even think of more general cases here. The implementation is extremely simple though: [https://github.com/JuliaObjects/Accessors.jl/blob/master/src/optics.jl#L412-L415](https://github.com/JuliaObjects/Accessors.jl/blob/master/src/optics.jl#L412-L415), and I think extension PRs would be welcome.

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [February 22, 2022, 5:26pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/16 "2022-02-22T17:26:57Z")

</div>

These are my takes:

Preserving the original array:

```julia
julia> function f(x,inds)
           y = Vector{eltype(x)}(undef,length(x) - length(inds))
           j = 0
           for i in Iterators.filter(!in(inds),eachindex(x))
               j += 1
               @inbounds y[j] = x[i]
           end
           return y
       end
f (generic function with 1 method)

julia> x = collect(1:10); inds = [3,5,6];

julia> @btime f($x,$inds)
  72.919 ns (1 allocation: 112 bytes)
7-element Vector{Int64}:
  1
  2
  4
  7
  8
  9
 10

```

Modifying the original array:

```julia
julia> function f!(x,inds)
           for i in Iterators.reverse(Iterators.filter(in(inds),eachindex(x)))
               deleteat!(x,i)
           end
           return x
       end
f! (generic function with 1 method)

julia> @btime f!(x,$inds) setup=(x=copy($x)) evals=1
  121.000 ns (0 allocations: 0 bytes)
7-element Vector{Int64}:
  1
  2
  4
  7
  8
  9
 10

```

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 22, 2022, 5:29pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/17 "2022-02-22T17:29:51Z")

</div>

@aplavin, yet @rfourquet’s `deleteat!()` solution still beats it for a single index:

```julia
A = rand(1000);
@btime deleteat!(copy($A), 123) # 553 ns (1 allocation: 7.94 KiB)
@btime @delete $A[123]; # 970 ns (3 allocations: 16.00 KiB)

```

---

<div class="post-metadata">

### Author: ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)
#### Post date: [February 22, 2022, 5:37pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/18 "2022-02-22T17:37:49Z")

</div>

> [@aplavin](#):
>
> Indeed, `@delete` only works for a single index now

While I’ve probably seen your package mentioned before, I would have never thought of looking at it for exclusive indexing. Nor from just a quick look at the README now. But it’s good to know of all these alternative ways to do this, hopefully we’ll get at a best way, for more than one index AND at the same time best syntax and something viable to merge into Julia.

The criteria to include in Julia is that it’s helpful for Julia itself. Well, I don’t see it off-hand how relevant it is… seems they can do without (or not?). At least I would like to know the full syntax of the most important competitors, R, Python, and MATLAB, and how they map to Julia in once place.

Please help with Julia’s (unofficial) wikibook and/or PR to Julia’s docs. I’m not sure how wanted it is to list every difference in the official docs. The former is also good to know of and maintain, easier to do than the official docs:

> **[Introducing Julia/Migrating From Other Languages - Wikibooks, open books for...](https://en.wikibooks.org/wiki/Introducing_Julia/Migrating_From_Other_Languages)**

---

<div class="post-metadata">

### Author: ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)
#### Post date: [February 22, 2022, 5:38pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/19 "2022-02-22T17:38:29Z")

</div>

Thanks, didn’t know that. Now I might make a PR to Accessors.jl with this performance improvement, as I often use its delete function.

---

<div class="post-metadata">

### Author: ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)
#### Post date: [February 22, 2022, 10:13pm UTC](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894/20 "2022-02-22T22:13:54Z")

</div>

> A = rand(1000);  
> @btime deleteat!(copy($A), 123) # 553 ns (1 allocation: 7.94 KiB)  
> @btime @delete $A[123]; # 970 ns (3 allocations: 16.00 KiB)

the following function also manages index arrays and has intermediate times with respect to those of the two functions above

```julia
function delat(v,idx)
mid=eltype(v)[]
shinds=idx[1:end] .-idx[1].+1
l=idx[1]
r=idx[end]
@inbounds for (i,e) in enumerate(v[l:r])
        if i ∉ shinds
            push!(mid,e)
        end
    end
[@view v[1:idx[1]-1]; mid; @view v[idx[end]+1:end]]
end

```

[Next page](https://discourse.julialang.org/t/elegant-way-of-exclusive-indexing/76894.md?page=2)
