Ruby sort by boolean and number

I am using Ruby 1.8.7. I have the following hash array. First I need to sort by a boolean, but these results should also be ordered in their original order. I basically need to transfer all the true hashes to the top of the array, but keep the original order.

Any help would be appreciated!

array = [{:id => 1, :accepts => false}, {:id => 2, :accepts => false}, {:id => 3, :accepts => true}, {:id => 4, :accepts => false}, {:id => 5, :accepts => true}] sorted = array.sort do |x, y| if x[:accepts] == y[:accepts] 0 elsif x[:accepts] == true -1 elsif x[:accepts] == false 1 end end 

This type that I have is:

5 - true
3 - true
2 - false 4 - false 1 - false

I need him to give:

3 - true
5 - true
1 - false 2 - false 4 - false

+6
sorting arrays ruby boolean
source share
7 answers

This task:

 array.sort{|a,b| (a[:accepts] == b[:accepts]) ? ((a[:id] < b[:id]) ? -1 : 1) : (a[:accepts] ? -1 : 1)} 
+7
source share

Use sort_by for these things, not sort !

 array.sort_by {|h| [h[:accepts] ? 0 : 1,h[:id]]} 
+14
source share
 array = [{:id => 1, :accepts => false}, {:id => 2, :accepts => false}, {:id => 3, :accepts => true}, {:id => 4, :accepts => false}, {:id => 5, :accepts => true}] sorted = array.sort do |x, y| if x[:accepts] ^ y[:accepts] x[:accepts] ? -1 : 1 else x[:id] <=> y[:id] end end puts sorted 

Or != Instead of ^ if you want.

+1
source share

Well, from your question that you are doing, you really wanted to group the results by value :accepts and combine both result sets back into one array. My solution for this would be:

 array.select {|where| where[:accepts] } | array.reject {|where| where[:accepts] } # => [{:accepts=>true, :id=>3}, # {:accepts=>true, :id=>5}, # {:accepts=>false, :id=>1}, # {:accepts=>false, :id=>2}, # {:accepts=>false, :id=>4}] 

This will maintain the original order without specifying a type :id . This means that you do not need an auxiliary key to maintain order, and you can keep the order of the result regardless of the data transferred.

This may be useful (and perhaps exactly what you need for further evaluations):

 array.group_by {|where| where[:accepts] } # => {false=>[{:accepts=>false, :id=>1}, # {:accepts=>false, :id=>2}, # {:accepts=>false, :id=>4}], # true=>[{:accepts=>true, :id=>3}, # {:accepts=>true, :id=>5}]} 

Again, there were no artificial varieties ... group_by is new in 1.8.7.

PS: If you do not want the first code fragment to remove duplicates from your array, replace the panel operator with the plus operator. "|" combines two sets in accordance with the theory of set theory (union), while "+" combines two sets (the result is not really a set, but a simple array).

+1
source share

You can add an additional key check :id if :accepts is:

 array = [{:id => 1, :accepts => false}, {:id => 2, :accepts => false}, {:id => 3, :accepts => true}, {:id => 4, :accepts => false}, {:id => 5, :accepts => true}] sorted = array.sort do |x, y| if x[:accepts] == y[:accepts] if x[:id] == y[:id] 0 elsif x[:id] > y[:id] 1 elsif x[:id] < y[:id] -1 end elsif x[:accepts] == true  -1 elsif x[:accepts] == false  1 end end 
0
source share
 a.sort_by { |x| (x[:accepts] ? 0 : 99999) + x[:id] } 

Update: well, obviously, this requires x[:id].respond_to? "+" x[:id].respond_to? "+" , and in addition, there are restrictions on its range with respect to constants.

This, however, is the shortest and probably the fastest answer, if also undoubtedly the most dubious.

The really important lesson is that it illustrates that you need to look beyond Array (or something else) and check Enumerable if it is in (your object).class.ancestors . These questions and their viewers often after answering the question "what should I learn about Ruby next, I suspect there are other ways."

Regardless of whether this is a good sorting method (admittedly dubious), this answer suggests #sort_by and just finding documents for #sort_by (it's not in an array) will teach a small but important lesson to a beginner.

0
source share

This is because sorting in Ruby 1.8.7 is unstable.

All you have to do is not return your sort block 0 :

 sorted = array.sort do |x, y| if x[:accepts] == y[:accepts] x[:id] <=> y[:id] # not 0 elsif x[:accepts] -1 else 1 end end 

(no need to explicitly compare the boolean with true and false )

0
source share

All Articles