# Unique (indices) method similar to MATLAB

**URL:** https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446
**Category:** General Usage
**Tags:** question
**Created:** [February 11, 2020, 12:35am UTC](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446 "2020-02-11T00:35:27Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)
#### Post date: [February 11, 2020, 12:35am UTC](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446/1 "2020-02-11T00:35:27Z")

</div>

Is there a method in Julia similar to MATLAB’s `unique` method? In Julia there is `unique`, but it does not have the same functionality. Specifically, I would like to get the list of indices of unique elements.

```nohighlight
julia> unique([9,2,9,5])
3-element Array{Int64,1}:
 9
 2
 5

julia> indices = [1,2,4]
3-element Array{Int64,1}:
 1
 2
 4

```

In Matlab, the `unique` method provides the indices in the 2nd output argument, but Julia does not.

Is there any existing similar method? [Unique values in array - MATLAB unique](https://www.mathworks.com/help/matlab/ref/unique.html)

---

<div class="post-metadata">

### Author: ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)
#### Post date: [February 11, 2020, 12:58am UTC](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446/2 "2020-02-11T00:58:14Z")

</div>

`x->findfirst.(.==(unique(x)), Ref(x) )` perhaps?

---

<div class="post-metadata">

### Author: ![lungben](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lungben/32/12314_2.png) [@lungben](https://discourse.julialang.org/u/lungben)
#### Post date: [February 11, 2020, 7:04am UTC](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446/3 "2020-02-11T07:04:21Z")

</div>

You can pass a function to `unique` in order to get the indices of the unique elements:

```julia
idx = unique(z -> x[z], 1:length(x))
x[idx]

```

---

<div class="post-metadata">

### Author: ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)
#### Post date: [February 11, 2020, 7:26am UTC](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446/4 "2020-02-11T07:26:12Z")

</div>

This comment still seems relevant: [Redirecting to Google Groups](https://groups.google.com/forum/#!topic/julia-users/205gVuPuZFU)

One reason I probably still never notice is that we have Dict:

```julia
itr = pairs(a)
ua = Dict{valtype(itr),keytype(itr)}()
for (idx, val) in itr
    ua[val] = idx
end

julia> ua
Dict{Int64,Int64} with 3 entries:
  9 => 3
  2 => 2
  5 => 4

```

You can use `Iterators.reverse` if you prefer the first instance rather than last instance.

I’m well aware that looks longer than `unique`, but I nevertheless stand by the sense that I never miss it. I think it’s because in circumstances where I’d use it, the Dict may be useful in its own right. You also don’t have to read any documentation if you do it this way, whereas with Matlab’s `unique` you’re always poring over the docs to remember which index output is which.

---

<div class="post-metadata">

### Author: ![lungben](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lungben/32/12314_2.png) [@lungben](https://discourse.julialang.org/u/lungben)
#### Post date: [February 11, 2020, 7:57am UTC](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446/5 "2020-02-11T07:57:17Z")

</div>

In the link @tim.holy posted there is a link to a Github Issue:

[https://github.com/JuliaLang/julia/issues/1845](https://github.com/JuliaLang/julia/issues/1845)

---

<div class="post-metadata">

### Author: ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)
#### Post date: [February 11, 2020, 10:18am UTC](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446/6 "2020-02-11T10:18:28Z")

</div>

For completeness, perhaps this thread should mention [GroupSlices.jl](https://github.com/mcabbott/GroupSlices.jl) (not written by me!) which in fact doesn’t return Matlab’s 3rd output:

```julia
A = [9, 2, 9, 5];
C = unique(A)
ia = unique(i -> A[i], 1:length(A)) # [1, 2, 4]
A[ia] == C
using GroupSlices
ig = groupslices(A) # [1, 2, 1, 4]
A[ig] == A # not matlab's ic
ia == firstinds(ig)

```

---

<div class="post-metadata">

### Author: ![t-bltg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/t-bltg/32/25526_2.png) [@t-bltg](https://discourse.julialang.org/u/t-bltg)
#### Post date: [December 8, 2023, 8:04pm UTC](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446/7 "2023-12-08T20:04:36Z")

</div>

Here is a pure Julia implementation of MATLAB’s `unique`, which returns `ic` contrary to the [previous answer](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446/6) using `GroupSlices` :

```julia
uniq(A; dims=1) = begin
  @assert ndims(A) ∈ (1, 2)
  slA = ndims(A) > 1 ? eachslice(A; dims) : A

  ia = unique(i -> slA[i], axes(A, dims))
  sort!(ia; by=i -> slA[i])

  C = stack(slA[ia]; dims)
  slC = ndims(A) > 1 ? eachslice(C; dims) : C

  ic = map(r -> findfirst(==(slA[r]), slC), axes(A, dims))

  C, ia, ic
end

```

And basic testing:

```julia
using Test

main() = begin
  let A = [
    3 1
    3 3
    1 3
    3 2
    2 3
    1 1
    1 2
    2 3
    3 3
    3 3
  ]
    C, ia, ic = uniq(A; dims=1)
    @test ia == [6, 7, 3, 5, 1, 4, 2]
    @test ic == [5, 7, 3, 6, 4, 1, 2, 4, 7, 7]
    @test C == A[ia, :]
    @test A == C[ic, :]

    C, ia, ic = uniq(A'; dims=2)
    @test ia == [6, 7, 3, 5, 1, 4, 2]
    @test ic == [5, 7, 3, 6, 4, 1, 2, 4, 7, 7]
    @test C == A'[:, ia]
    @test A' == C[:, ic]
  end

  let A = [9, 2, 9, 5]
    C, ia, ic = uniq(A)
    @test ia == [2, 4, 1]
    @test ic == [3, 1, 3, 2]
    @test C == A[ia]
    @test A == C[ic]
  end
end

main()

```

---

<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: [December 8, 2023, 10:43pm UTC](https://discourse.julialang.org/t/unique-indices-method-similar-to-matlab/34446/8 "2023-12-08T22:43:15Z")

</div>

Useful piece of functionaly indeed! The major challenge is where to put it to make discoverable 🙂  
I added `uniqueview()` into DataManipulation.jl quite some time ago, its result defines existing Julia functions whenever possible to retrieve various indices:

```julia
julia> using DataManipulation

julia> A = [9, 2, 9, 5]

julia> uv = uniqueview(A) # like unique(), but a view
3-element DataManipulation.UniqueView{Int64, Vector{Int64}, Vector{SubArray{Int64, 1, Vector{Int64}, Tuple{UnitRange{Int64}}, true}}}:
 9
 2
 5

# parentindices() is a Base Julia function, specifically for views - makes sense here:
julia> parentindices(uv)
([1, 2, 4],)

# these are used to go from A to uv:
julia> A[parentindices(uv)[1]]
3-element Vector{Int64}:
 9
 2
 5

# no Base function to go in the other direction, so we define a new one:
julia> using DataManipulation: inverseindices

julia> inverseindices(uv)
4-element Vector{Int64}:
 1
 2
 1
 3

# this is to go back from uv to A
julia> uv[inverseindices(uv)]
4-element Vector{Int64}:
 9
 2
 9
 5

```

Btw, working with indices manually isn’t needed for lots of usecases. For example, apply a function to all elements, performing actual computations only for distinct values:

```julia
julia> @modify(a -> a + rand(), A |> uniqueview |> Elements())
4-element view(::Vector{Float64}, [1, 2, 1, 3]) with eltype Float64:
 9.523911282035403 # 1 and 3 elements are exactly the same, rand() only computed once for them
 2.8311892880800316
 9.523911282035403
 5.5151051390572094

```

or assign distinct numbers to unique values:

```julia
julia> @modify(Au -> 1:length(Au), A |> uniqueview)
4-element view(::UnitRange{Int64}, [1, 2, 1, 3]) with eltype Int64:
 1
 2
 1
 3

```

Here, `@modify` comes from Accessors.jl – reexported by DataManipulation.jl.
