Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort_by Ruby, One Descending, One Ascending

Tags:

ruby

I have searched for an answer on this to no avail, there is a similar question but the answer did not work in this situation, it sorts on a numeric item. Similar Question -That did not work I am trying to use ruby's sort_by to sort one item descending with the other ascending. All I can find is one or the other.

Here is the code:

# Primary sort Last Name Descending, with ties broken by sorting Area of interest.
people = people.sort_by { |a| [ a.last_name, a.area_interest]}

Any guidance would certainly assist.

Sample data:

input

  • Russell, Logic
  • Euler, Graph Theory
  • Galois, Abstract Algebra
  • Gauss, Number Theory
  • Turing, Algorithms
  • Galois, Logic

output

  • Turing , Algorithms
  • Russell, Logic
  • Gauss, Number Theory
  • Galois, Abstract Algebra
  • Galois, Logic
  • Euler, Graph Theory
like image 209
diek Avatar asked Mar 01 '14 19:03

diek


People also ask

Is true ascending or descending?

False means that the list will be sorted in ascending order. True means that the list will be sorted in descending (reverse) order.


2 Answers

This is a straightforward way:

a = [ ['Russell', 'Logic'],           ['Euler', 'Graph Theory'],
      ['Galois', 'Abstract Algebra'], ['Gauss', 'Number Theory'],
      ['Turing', 'Algorithms'],       ['Galois', 'Logic'] ]

a.sort { |(name1,field1),(name2,field2)|
  (name1 == name2) ? field1 <=> field2 : name2 <=> name1 }
#=> [ ["Turing", "Algorithms"],   ["Russell", "Logic"],
#     ["Gauss", "Number Theory"], ["Galois", "Abstract Algebra"],
#     ["Galois", "Logic"],        ["Euler", "Graph Theory"] ]

For multiple fields, sorting on the first in descending order, then on each of the others, in sequence, in ascending order:

a = [ %w{a b c}, %w{b a d}, %w{a b d}, %w{b c a}, %w{a b c}, %w{b c b}]
  #=> [["a", "b", "c"], ["b", "a", "d"], ["a", "b", "d"],
  #    ["b", "c", "a"], ["a", "b", "c"], ["b", "c", "b"]] 

a.sort { |e,f| e.first == f.first ? e[1..-1] <=> f[1..-1] : f <=> e }
  #=> [["b", "a", "d"], ["b", "c", "a"], ["b", "c", "b"],
  #    ["a", "b", "c"], ["a", "b", "c"], ["a", "b", "d"]] 
like image 55
Cary Swoveland Avatar answered Sep 24 '22 07:09

Cary Swoveland


Make a custom class that invert the result of <=> (including Comparable).

Wrap the object you want sort descending with the custom class.

Example:

class Descending
  include Comparable
  attr :obj

  def initialize(obj)
    @obj = obj
  end
  def <=>(other)
    return -(self.obj <=> other.obj)
  end
end

people = [
  {last_name: 'Russell', area_interest: 'Logic'},
  {last_name: 'Euler', area_interest: 'Graph Theory'},
  {last_name: 'Galois', area_interest: 'Abstract Algebra'},
  {last_name: 'Gauss', area_interest: 'Number Theory'},
  {last_name: 'Turing', area_interest: 'Algorithms'},
  {last_name: 'Galois', area_interest: 'Logic'},
]
puts people.sort_by {|person| [
  Descending.new(person[:last_name]),  # <---------
  person[:area_interest],
]}

output:

{:last_name=>"Turing", :area_interest=>"Algorithms"}
{:last_name=>"Russell", :area_interest=>"Logic"}
{:last_name=>"Gauss", :area_interest=>"Number Theory"}
{:last_name=>"Galois", :area_interest=>"Abstract Algebra"}
{:last_name=>"Galois", :area_interest=>"Logic"}
{:last_name=>"Euler", :area_interest=>"Graph Theory"}

BTW, if the object you want sort descending is a numeric value, you can simply use unary operator -:

people.sort_by {|person| [-person.age, person.name] }
like image 36
falsetru Avatar answered Sep 24 '22 07:09

falsetru