# Sort indices based on value

**URL:** <https://discourse.julialang.org/t/sort-indices-based-on-value/107299>\
**Category:** New to Julia\
**Created:** [December 7, 2023, 8:21pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299 "2023-12-07T20:21:40Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![putianyi888](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/putianyi888/32/32279_2.png) [@putianyi888](https://discourse.julialang.org/u/putianyi888)\
**Post date:** [December 7, 2023, 8:21pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/1 "2023-12-07T20:21:40Z")

</div>

Is there an existing method that can get a `value -> index` map of a vector? For example, something that works like this:

```julia
function findalls(A::Vector)
    d = Dict{eltype(A), Vector{Int}}()
    for (i,v) in enumerate(A)
        if !haskey(d, v)
            d[v] = Vector{Int}()
        end
        push!(d[v], i)
    end
    d
end

```

EDIT:  
My benchmark results are:

- @bertschi 's approach is 2x faster than the second candidate at least on my use case (large vector, very few unique entries).
- My own approach and @Dan 's one-line approach are the same, except that `haskey` is faster than `get!`, making my approach 1/3 faster. (if i use `get!` in my approach then they have the same performance)
- `FlexiGroups.groupfind` is on par with @Dan 's approach while having more number of allocations and less overall size of allocations.
- `SplitApplyCombine.groupfind` is 3.5x slower with 500x more allocations.

---

<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:** [December 7, 2023, 8:30pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/2 "2023-12-07T20:30:25Z")

</div>

I’m not sure if this is what you want, but maybe:

```julia
julia> x = ["a", "b", "b", "c"];

julia> collect(unique(first, zip(x, eachindex(x))))
3-element Vector{Tuple{String, Int64}}:
 ("a", 1)
 ("b", 2)
 ("c", 4)

```

Or to get a Dict:

```julia
julia> Dict(key => val for (key, val) in unique(first, zip(x, eachindex(x))))
Dict{String, Int64} with 3 entries:
  "c" => 4
  "b" => 2
  "a" => 1

```

---

<div class="post-metadata">

**Author:** ![putianyi888](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/putianyi888/32/32279_2.png) [@putianyi888](https://discourse.julialang.org/u/putianyi888)\
**Post date:** [December 7, 2023, 8:35pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/3 "2023-12-07T20:35:58Z")

</div>

In your example I expect the result to be

```julia
"c" => [4]
"b" => [2,3]
"a" => [1]

```

Either tuple or dict works for me.

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [December 7, 2023, 8:47pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/4 "2023-12-07T20:47:30Z")

</div>

How about:

```julia
indexall(x) = foldl((d,(k,v))->(push!(get!(()->Int[],d,v),k); d),
  pairs(x); init=Dict{eltype(x),Vector{Int}}())

```

which gives:

```julia
julia> x = ["a", "b", "b", "c"];

julia> indexall(x)
Dict{String, Vector{Int64}} with 3 entries:
  "c" => [4]
  "b" => [2, 3]
  "a" => [1]

```

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [December 7, 2023, 8:54pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/5 "2023-12-07T20:54:58Z")

</div>

Maybe not the fastest, but rather short:

```julia
map(e -> e => findall(==(e), x), unique(x))

```

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [December 7, 2023, 8:59pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/6 "2023-12-07T20:59:59Z")

</div>

Honestly, what you’ve written in the start is great, @putianyi888 (just make the dict be a `Dict{eltype(A), Vector{Int}}()`). There’s nothing magical about built-in/existing functionality — the stuff you write can perform just as well.

Run with it!

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [December 7, 2023, 9:05pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/7 "2023-12-07T21:05:54Z")

</div>

If you go with function in OP, the following:

> [@putianyi888](#):
>
> `d = Dict{eltype(A), Int}()`

needs to be:

```julia
d = Dict{eltype(A), Vector{Int}}()

```

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [December 7, 2023, 9:22pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/8 "2023-12-07T21:22:05Z")

</div>

Also,

```julia
findalls2(x) = begin
    d = Dict{eltype(x),Vector{Int}}()
    push!.(get!.(()->Int[], Ref(d), values(x)), keys(x))
    return d
end

```

does the same, but exercises broadcasting a bit.

---

<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 7, 2023, 10:40pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/9 "2023-12-07T22:40:54Z")

</div>

Try:

```julia
using FlexiGroups

groupfind(A)

```

---

<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:** [December 8, 2023, 1:21am UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/10 "2023-12-08T01:21:55Z")

</div>

What is the difference with:

```julia
using SplitApplyCombine
groupfind(A)

```

---

<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, 2:56am UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/11 "2023-12-08T02:56:18Z")

</div>

The function is clearly inspired by SplitApplyCombine (:  
For regular `Vector`s the difference is basically just performance, FlexiGroups are typically (always?) faster.  
More generally, see a list of main differences at [Alexander Plavin / FlexiGroups.jl · GitLab](https://gitlab.com/aplavin/FlexiGroups.jl). Better support for different collection types, some helper features around grouping. I tried getting some of these into SplitApplyCombine, but didn’t manage to – so, created a more focused package.

---

<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:** [December 8, 2023, 3:00am UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/12 "2023-12-08T03:00:09Z")

</div>

```julia
using FlexiGroups
help?> groupfind
search: groupfind

  No documentation found.

```

---

<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, 7:22pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/13 "2023-12-08T19:22:07Z")

</div>

Yeah I didn’t expect `groupfind` to actually be useful (:  
I use `group()`/`groupview()`/`groupmap()` functions, but almost never really needed `groupfind()` myself. Added it simply because such a function already existed in SplitApplyCombine. Others (`group()`/…) have docstrings, maybe now it’s a sign that I should add one for `groupfind` as well.

---

<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:** [December 8, 2023, 7:28pm UTC](https://discourse.julialang.org/t/sort-indices-based-on-value/107299/14 "2023-12-08T19:28:46Z")

</div>

I might experiment with some names. eg `groupfindall`, `groupindices`.
