Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom sort array of hashes based on multiple key/value pairs

Tags:

sorting

ruby

I have an array of hashes which I need to sort based on two different key value pairs.

Here is the array I am trying to sort:

array_group =  [
 {operator: OR, name: "some string", status: false},
 {operator: AND, name: "other string", status: false},
 {operator: _NOT_PRESENT, name: "another string", status: true},
 {operator: AND, name: "just string", status: true}
]

I want to sort array_group so I have items with status: true first, followed by status: false, followed by the items with operator: _NOT_PRESENT and finally sort it based on name, resulting in something like:

array_group = [
 {operator: AND, name: "just string", status: true},
 {operator: AND, name: "other string", status: false},
 {operator: OR, name: "some string", status: false},
 {operator: _NOT_PRESENT, name: "another string", status: true},
]

Is there a way that I can get this done without creating sub-arrays and sorting them and concatenating them back?

like image 398
sujay Avatar asked Nov 06 '15 20:11

sujay


People also ask

How do I sort an array of hashes in Perl?

Perl's built in sort function allows us to specify a custom sort order. Within the curly braces Perl gives us 2 variables, $a and $b, which reference 2 items to compare. In our case, these are hash references so we can access the elements of the hash and sort by any key we want.

Can you sort a hash by values?

##Hash#keys and Hash#values A simple way to sort a hash keys or values is to use the methods Hash#keys and Hash#values. Those 2 methods are simply returning an array of the keys or the values of your hash. So as soon as you get an array with only one of the type of values you want to work with, it's all good!

Can you sort elements in a Ruby hash object?

You can use the sort method on an array, hash, or another Enumerable object & you'll get the default sorting behavior (sort based on <=> operator) You can use sort with a block, and two block arguments, to define how one object is different than another (block should return 1, 0, or -1)


2 Answers

You can also use Enumerable#sort_by. The example builds an array which is compared element by element when sorting.

array_group.sort_by { |e| [e[:operator] == "_NOT_PRESENT" ? 1 : 0, 
                           e[:status] ? 0 : 1, 
                           e[:name]] }

The example above orders records with operator: "_NOT_PRESENT" also by :status. The following snippet precisely performs the ordering from the question.

def priority(h)
  case
  when h[:operator] == "_NOT_PRESENT" then 3
  when h[:status] == false            then 2
  # h[:status] == true
  else 1
  end
end

array_group.sort_by { |e| [priority(e), e[:name]] }
like image 63
sschmeck Avatar answered Sep 22 '22 01:09

sschmeck


You can use the Array.sort method. It accepts a block with two arguments (x, y), when x is larger than y it should return 1, otherwise -1, and 0 if they are equal.

The code:

OR = "OR"
AND = "AND"
_NOT_PRESENT = "_NOT_PRESENT"

array_group =  [
 {operator: OR, name: "some string", status: false},
 {operator: AND, name: "other string", status: true},
 {operator: _NOT_PRESENT, name: "another string", status: true},
 {operator: AND, name: "just string", status: true}
]

results = array_group.sort do |x, y|
  next x[:operator] == _NOT_PRESENT ? 1 : -1 if x[:operator] == _NOT_PRESENT || y[:operator] == _NOT_PRESENT
  next x[:status] ? -1 : 1 if x[:status] != y[:status]
  next x[:name] <=> y[:name]
end

And btw, you your input and output data doesn't match each other — the hash with OR is false in the input, but true in the output.

I believe your output should look like:

[{:operator=>"AND", :name=>"just string", :status=>true},
 {:operator=>"AND", :name=>"other string", :status=>true},
 {:operator=>"OR", :name=>"some string", :status=>false},
 {:operator=>"_NOT_PRESENT", :name=>"another string", :status=>true}]

That output will actually match your sorting logic.

like image 36
Dmitry Sokurenko Avatar answered Sep 19 '22 01:09

Dmitry Sokurenko