Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is sort in Ruby stable?

Tags:

sorting

ruby

Is sort in Ruby stable? That is, for elements that are in a tie for sort, is the relative order among them preserved from the original order? For example, given:

a = [   {id: :a, int: 3},   {id: :b, int: 1},   {id: :c, int: 2},   {id: :d, int: 0},   {id: :e, int: 1},   {id: :f, int: 0},   {id: :g, int: 1},   {id: :h, int: 2}, ] 

is it guaranteed that we always get for

a.sort_by{|h| h[:int]} 

the following

[   {id: :d, int: 0},   {id: :f, int: 0},   {id: :b, int: 1},   {id: :e, int: 1},   {id: :g, int: 1},   {id: :c, int: 2},   {id: :h, int: 2},   {id: :a, int: 3}, ] 

without any variation for the relative order among the elements with the :id value :d, :f, and among :b, :e, :g, and among :c, :h? If that is the case, where in the documentation is it described?

This question may or may not have connection with this question.

like image 726
sawa Avatar asked Mar 15 '13 21:03

sawa


People also ask

Is bubble sort stable or not?

Bubble sort is a stable sorting algorithm, because, it maintains the relative order of elements with equal values after sorting. Bubble sort algorithm repeatedly compares the adjacent elements and swaps them if not in order.

How does sort work in Ruby?

The Ruby sort method works by comparing elements of a collection using their <=> operator (more about that in a second), using the quicksort algorithm. You can also pass it an optional block if you want to do some custom sorting. The block receives two parameters for you to specify how they should be compared.

Is quick sort stable or unstable?

QuickSort is an unstable algorithm because we do swapping of elements according to pivot's position (without considering their original positions).

Does Selection sort stable?

Hence Selection sort is non-adaptable. Selection sort is NOT a stable sorting algorithm. Elements which are equal might be re-arranged in the final sort order relative to one another.


2 Answers

Both MRI's sort and sort_by are unstable. Some time ago there was a request to make them stable, but it was rejected. The reason: Ruby uses an in-place quicksort algorithm, which performs better if stability is not required. Note that you can still implement stable methods from unstable ones:

module Enumerable   def stable_sort     sort_by.with_index { |x, idx| [x, idx] }   end    def stable_sort_by     sort_by.with_index { |x, idx| [yield(x), idx] }   end end 
like image 70
tokland Avatar answered Sep 19 '22 15:09

tokland


No, ruby's built-in sort is not stable.

If you want stable sort, this should work. You probably want to create a method for it if you're going to use it often.

a.each_with_index.sort_by {|h, idx| [h[:int], idx] }.map(&:first) 

Basically it keeps track of the original array index of each item, and uses it as a tie-breaker when h[:int] is the same.

More info, for the curious:

As far as I know, using the original array index as the tie-breaker is the only way to guarantee stability when using an unstable sort. The actual attributes (or other data) of the items will not tell you their original order.

Your example is somewhat contrived because the :id keys are sorted ascending in the original array. Suppose the original array were sorted descending by :id; you'd want the :id's in the result to be descending when tie-breaking, like so:

[  {:id=>:f, :int=>0},  {:id=>:d, :int=>0},  {:id=>:g, :int=>1},  {:id=>:e, :int=>1},  {:id=>:b, :int=>1},  {:id=>:h, :int=>2},  {:id=>:c, :int=>2},  {:id=>:a, :int=>3} ] 

Using the original index will handle this too.

Update:

Matz's own suggestion (see this page) is similar, and may be slightly more efficient than the above:

n = 0 ary.sort_by {|x| n+= 1; [x, n]} 
like image 35
Kelvin Avatar answered Sep 21 '22 15:09

Kelvin