# How to sort two or more lists at once?

**URL:** <https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073>\
**Category:** General Usage\
**Tags:** question, sort, sortperm\
**Created:** [June 29, 2018, 8:56pm UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073 "2018-06-29T20:56:12Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![djsegal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djsegal/32/13752_2.png) [@djsegal](https://discourse.julialang.org/u/djsegal)\
**Post date:** [June 29, 2018, 8:56pm UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/1 "2018-06-29T20:56:12Z")

</div>

Let’s say you have:

```julia
cur_x = shuffle(1:10)
cur_y = rand(10)

```

And you want to sort `cur_x` in the usual fashion, but drag `cur_y` along for the ride.

How do you – in one line – `zip(cur_x, cur_y)` and `sort!` by `cur_x`?

// so in the end it looks like:

```julia
cur_x = [1 2 3 4 5 6 7 8 9 10]

```

---

<div class="post-metadata">

**Author:** ![stillyslalom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stillyslalom/32/45687_2.png) [@stillyslalom](https://discourse.julialang.org/u/stillyslalom)\
**Post date:** [June 29, 2018, 9:14pm UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/2 "2018-06-29T21:14:28Z")

</div>

`p = sortperm(cur_x); cur_x .= cur_x[p]; cur_y .= cur_y[p]` ?

---

<div class="post-metadata">

**Author:** ![djsegal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djsegal/32/13752_2.png) [@djsegal](https://discourse.julialang.org/u/djsegal)\
**Post date:** [June 29, 2018, 10:31pm UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/3 "2018-06-29T22:31:25Z")

</div>

Seems like a pretty common operation.

Is there anything more succinct than that?

---

<div class="post-metadata">

**Author:** ![anon94023334](https://avatars.discourse-cdn.com/v4/letter/a/e274bd/32.png) [@anon94023334](https://discourse.julialang.org/u/anon94023334)\
**Post date:** [June 29, 2018, 10:52pm UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/4 "2018-06-29T22:52:22Z")

</div>

Does

```julia
sort(collect(zip(cur_x, cur_y)); by=first)

```

get you closer to where you want to be?

---

<div class="post-metadata">

**Author:** ![djsegal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djsegal/32/13752_2.png) [@djsegal](https://discourse.julialang.org/u/djsegal)\
**Post date:** [June 29, 2018, 11:08pm UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/5 "2018-06-29T23:08:27Z")

</div>

I definitely like that, but it’s already ~40 characters? // and not done in place

How would you succinctly unzip it for assignment then?

> <https://stackoverflow.com/questions/36367482/unzip-an-array-of-tuples-in-julia>

* * *

**edit:** I guess something like this works:

```julia
cur_x, cur_y = map(collect,zip(sort(collect(zip(cur_x, cur_y)); by=first)...))

```

but that’s ~80 characters and I’d probably just go with @stillyslalom’s answer then?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [June 30, 2018, 6:02am UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/6 "2018-06-30T06:02:21Z")

</div>

If you want in-place, definitely go with @stillyslalom’s answer.

Also, I am not sure that being more “succinct” than this is a good goal (even if it was feasible), but I generally prize clarity more than brevity. Just now I was upgrading some package of mine for v0.7, and found that one of the unit tests was broken, but frankly I spent an hour figuring out what it does… and I wrote it 5 months ago.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [June 30, 2018, 8:01am UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/7 "2018-06-30T08:01:01Z")

</div>

A variation of the `sortperm` approach is

```julia
cur_x, cur_y = getindex.((cur_x, cur_y), (sortperm(cur_x),))

```

This could potentially be more succinct with dotted indexing (see e.g. [https://github.com/JuliaLang/julia/issues/22858](https://github.com/JuliaLang/julia/issues/22858)) but it has its complications.

This solution is not in place but on the other hand `cur_x .= cur_x[p]` computes a temporary on the right hand side, which is then discarded, so unless it’s important not to replace the original arrays there’s no big difference.

---

<div class="post-metadata">

**Author:** ![Liso](https://avatars.discourse-cdn.com/v4/letter/l/898d66/32.png) [@Liso](https://discourse.julialang.org/u/Liso)\
**Post date:** [June 30, 2018, 8:28am UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/8 "2018-06-30T08:28:41Z")

</div>

> [@GunnarFarneback](#):
>
> This solution is not in place but on the other hand `cur_x .= cur_x[p]` computes a temporary on the right hand side, which is then discarded, so unless it’s important not to replace the original arrays there’s no big difference.

“in-place” is also more disaster-prone.

---

<div class="post-metadata">

**Author:** ![johnh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/johnh/32/3615_2.png) [@johnh](https://discourse.julialang.org/u/johnh)\
**Post date:** [July 2, 2018, 9:24am UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/9 "2018-07-02T09:24:04Z")

</div>

> [@Tamas\_Papp](#):
>
> Also, I am not sure that being more “succinct” than this is a good goal (even if it was feasible), but I generally prize clarity more than brevity.

This. Looking back at Perl, it was meant to read like English (sorry, I know one should not be English-centric). However look what happens to Perl when people put in shall we say ‘ingenious’ constructs to serve brevity.  
One day an Obfuscated Julia contest may be fun, but I honestly think Obfuscated Perl contests did the language no favours.

Lets also consider Python. One of the joys I find with Python is that you can understand the program flow even if you are not familiar with the language.  
So a small plea from me. Do not try to compress the character count down to the point of losing the meaning.  
Bits are cheap these days. Remember “Premature optimization is the root of all evil” and I think this counts for the amount of space program code and also data structures consume.

---

<div class="post-metadata">

**Author:** ![djsegal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djsegal/32/13752_2.png) [@djsegal](https://discourse.julialang.org/u/djsegal)\
**Post date:** [July 2, 2018, 11:51am UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/10 "2018-07-02T11:51:00Z")

</div>

Thanks for the suggestions! Made a pair of functions from these called: `sort_lists!` and `shuffle_lists!`:

```julia
function sort_lists!(main_list, other_lists...; cur_func::Function=identity)
    cur_indices = sortperm(main_list, by=cur_func)

    cur_list = main_list[cur_indices]
    _arrange_lists!(main_list, other_lists, cur_indices)
    cur_list
end

```

```julia
function shuffle_lists!(main_list, other_lists...)
    cur_indices = shuffle(1:length(main_list))

    cur_list = main_list[cur_indices]
    _arrange_lists!(main_list, other_lists, cur_indices)
    cur_list
end

```

```julia
function _arrange_lists!(main_list, other_lists, cur_indices)
    cur_lists = [collect(main_list)]
    isempty(other_lists) || append!(cur_lists, other_lists)
    
    for cur_list in cur_lists
        cur_list .= cur_list[cur_indices]
    end   
end

```

* * *

Quickly testing them out:

```julia
using Plots
a = linspace(-1,1,11)

b = (x -> x).(a)
c = (x -> x^2).(a)
d = (x -> x^3).(a)

p1 = plot(a,[b,c,d], legend=false)

a = shuffle_lists!(a,b,c,d)
p2 = plot(a,[b,c,d], legend=false)

a = sort_lists!(a,b,c,d)
p3 = plot(a,[b,c,d], legend=false)

plot(p1,p2,p3,layout=@layout([p1 p2 p3]))

```

![list_sorter](https://global.discourse-cdn.com/julialang/original/3X/5/8/58990b6d66ca52f9349c5fbd8b6796e41fa3f7c7.png)

---

<div class="post-metadata">

**Author:** ![piever](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/piever/32/1815_2.png) [@piever](https://discourse.julialang.org/u/piever)\
**Post date:** [July 2, 2018, 4:03pm UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/11 "2018-07-02T16:03:10Z")

</div>

For this case, you probably want to put things in a table:

```julia
using IndexedTables
s = table(rand(10), rand(10), rand(10))
sort(s, 1)
sort!(s, 2)

```

Extra benefit: you can sort by more than one column at ones (useful if there are repeated values).

---

<div class="post-metadata">

**Author:** ![nalimilan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nalimilan/32/147_2.png) [@nalimilan](https://discourse.julialang.org/u/nalimilan)\
**Post date:** [July 2, 2018, 4:07pm UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/12 "2018-07-02T16:07:48Z")

</div>

At this point you could as well put the two vectors in a data frame and sort its rows.

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [March 8, 2019, 10:43am UTC](https://discourse.julialang.org/t/how-to-sort-two-or-more-lists-at-once/12073/13 "2019-03-08T10:43:44Z")

</div>

This is a bit old, but let me just point out that Julia beautifully allows one to solve this problem by defining a custom type that holds a number of arrays, and teaching it how to sort itself. Then `sort!` can sort using the values in the first array, but updating the order for the second array too. This allows a solution that is faster, and with less allocations, than the `sortperm` approach. But of course it requires quite a bit more code

```julia
struct CoSorterElement{T1,T2}
    x::T1
    y::T2
end
struct CoSorter{T1,T2,S<:AbstractArray{T1},C<:AbstractArray{T2}} <: AbstractVector{CoSorterElement{T1,T2}}
    sortarray::S
    coarray::C
end

Base.size(c::CoSorter) = size(c.sortarray)
Base.getindex(c::CoSorter, i...) = 
    CoSorterElement(getindex(c.sortarray, i...), getindex(c.coarray, i...))
Base.setindex!(c::CoSorter, t::CoSorterElement, i...) = 
    (setindex!(c.sortarray, t.x, i...); setindex!(c.coarray, t.y, i...); c) 
Base.isless(a::CoSorterElement, b::CoSorterElement) = isless(a.x, b.x)
Base.Sort.defalg(v::C) where {T<:Union{Number, Missing}, C<:CoSorter{T}} = 
    Base.DEFAULT_UNSTABLE

```

With this you can do

```julia
julia> cur_x = rand(1:10, 10); cur_y = rand(10); using BenchmarkTools

julia> c = CoSorter(cur_x, cur_y); @btime sort!($c)
  30.111 ns (0 allocations: 0 bytes)
10-element CoSorter{Int64,Float64,Array{Int64,1},Array{Float64,1}}:
 CoSorterElement{Int64,Float64}(1, 0.40265954276014937)  
 CoSorterElement{Int64,Float64}(3, 0.07364824869474873)  
 CoSorterElement{Int64,Float64}(4, 0.7341851985737331)   
 CoSorterElement{Int64,Float64}(5, 0.0010439543093452297)
 CoSorterElement{Int64,Float64}(6, 0.8187812476982932)   
 CoSorterElement{Int64,Float64}(6, 0.8235149239112314)   
 CoSorterElement{Int64,Float64}(7, 0.891985683971432)    
 CoSorterElement{Int64,Float64}(7, 0.5556465187657322)   
 CoSorterElement{Int64,Float64}(8, 0.9217791949844834)   
 CoSorterElement{Int64,Float64}(10, 0.32895616976956177) 

```

The original arrays have now been co-sorted in-place.

For comparison, I think the next fastest is @anon94023334’s solution

```julia
julia> @btime sort(collect(zip($cur_x, $cur_y)); by=first);
  111.781 ns (5 allocations: 608 bytes)

```

EDIT: some corrections
