2

I'm trying to sort an array of hashs based on multiple keys and multiple directions (ASC and DESC).

Assume the array looks like:

items = [ {field1: '1', field2: 5, field3: 5}, 
          {field1: '1', field2: 1, field3: 3}, 
          {field1: '3', field2: 3, field3: 2},
          {field1: '3', field2: 1, field3: 8}, 
          {field1: '7', field2: 5, field3: 6}, 
          {field1: '7', field2: 5, field3: 1} ]

I want to create multiple_sort(items,options) method which works like:

multiple_sort(items, [{field: 'feed1', dir: 'asc'}, {field: 'feed3', dir: 'asc'}])

Will generate:

        [ {field1: '1', field2: 1, field3: 3},
          {field1: '1', field2: 5, field3: 5}, 
          {field1: '3', field2: 3, field3: 2},
          {field1: '3', field2: 1, field3: 8}, 
          {field1: '7', field2: 5, field3: 1}, 
          {field1: '7', field2: 5, field3: 6} ]

And

multiple_sort(items, [{field: 'feed1', dir: 'asc'}, {field: 'feed3', dir: 'desc'}])

Will output:

[ {field1: '1', field2: 5, field3: 5},
      {field1: '1', field2: 1, field3: 3}
      {field1: '3', field2: 1, field3: 8},
      {field1: '3', field2: 3, field3: 2}, 
      {field1: '7', field2: 5, field3: 6}, 
      {field1: '7', field2: 5, field3: 1} ]

Any help would be appreciated.

2 Answers 2

3

I took the liberty of interpreting feed1 as field1, and changing the strings to symbols for efficiency.

def multiple_sort(array, criteria)
  array.sort do |a, b|
    cmp = 0
    criteria.each do |criterion|
      cmp = a[criterion[:field]] <=> b[criterion[:field]]
      cmp = -cmp if criterion[:dir] == :desc
      break if cmp != 0
    end
    cmp
  end
end


require 'pp'

pp multiple_sort(items, [
  {field: :field1, dir: :asc},
  {field: :field3, dir: :asc}
])
# [{:field1=>"1", :field2=>1, :field3=>3},
#  {:field1=>"1", :field2=>5, :field3=>5},
#  {:field1=>"3", :field2=>3, :field3=>2},
#  {:field1=>"3", :field2=>1, :field3=>8},
#  {:field1=>"7", :field2=>5, :field3=>1},
#  {:field1=>"7", :field2=>5, :field3=>6}]

pp multiple_sort(items, [
  {field: :field1, dir: :asc},
  {field: :field3, dir: :desc}
])
# [{:field1=>"1", :field2=>5, :field3=>5},
#  {:field1=>"1", :field2=>1, :field3=>3},
#  {:field1=>"3", :field2=>1, :field3=>8},
#  {:field1=>"3", :field2=>3, :field3=>2},
#  {:field1=>"7", :field2=>5, :field3=>6},
#  {:field1=>"7", :field2=>5, :field3=>1}]
Sign up to request clarification or add additional context in comments.

Comments

1

You could use Enumerable#sort_by:

Code

def multiple_sort(items, options)
  items.sort_by { |h| options.map { |g|
    ((g[:dir]=='asc') ? 1 : -1) * h[g[:field].to_sym].to_i } }
end

Examples

items = [ {field1: '1', field2: 5, field3: 5}, 
          {field1: '1', field2: 1, field3: 3}, 
          {field1: '3', field2: 3, field3: 2},
          {field1: '3', field2: 1, field3: 8}, 
          {field1: '7', field2: 5, field3: 6}, 
          {field1: '7', field2: 5, field3: 1} ]

options = [{field: 'field1', dir: 'asc'}, {field: 'field3', dir: 'asc'}]
multiple_sort(items, options)
     #=> [{:field1=>"1", :field2=>1, :field3=>3},
     #    {:field1=>"1", :field2=>5, :field3=>5},
     #    {:field1=>"3", :field2=>3, :field3=>2},
     #    {:field1=>"3", :field2=>1, :field3=>8},
     #    {:field1=>"7", :field2=>5, :field3=>1},
     #    {:field1=>"7", :field2=>5, :field3=>6}] 

options = [{field: 'field1', dir: 'desc'}, {field: 'field3', dir: 'desc'}]
multiple_sort(items, options)
  # => [{:field1=>"7", :field2=>5, :field3=>6},
  #     {:field1=>"7", :field2=>5, :field3=>1},
  #     {:field1=>"3", :field2=>1, :field3=>8},
  #     {:field1=>"3", :field2=>3, :field3=>2},
  #     {:field1=>"1", :field2=>5, :field3=>5},
  #     {:field1=>"1", :field2=>1, :field3=>3}] 

items << {:field1=>"1", :field2=>6, :field3=>5}
  #=> [{:field1=>"1", :field2=>5, :field3=>5},
  #    {:field1=>"1", :field2=>1, :field3=>3},
  #    {:field1=>"3", :field2=>3, :field3=>2},
  #    {:field1=>"3", :field2=>1, :field3=>8},
  #    {:field1=>"7", :field2=>5, :field3=>6},
  #    {:field1=>"7", :field2=>5, :field3=>1},
  #    {:field1=>"1", :field2=>6, :field3=>5}] 

options = [{field: 'field1', dir: 'asc'}, {field: 'field3', dir: 'desc'},
           {field: 'field2', dir: 'desc'}]
multiple_sort(items, options)
  #=> [{:field1=>"1", :field2=>6, :field3=>5},
  #    {:field1=>"1", :field2=>5, :field3=>5},
  #    {:field1=>"1", :field2=>1, :field3=>3},
  #    {:field1=>"3", :field2=>1, :field3=>8},
  #    {:field1=>"3", :field2=>3, :field3=>2},
  #    {:field1=>"7", :field2=>5, :field3=>6},
  #    {:field1=>"7", :field2=>5, :field3=>1}] 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.