Comparing and sorting an array of hashes in Ruby

I have two arrays. They have different attributes.

array1 = [{name: "apple", quantity: 2}, {name: "grape", quantity: 10}, {name: "pear", quantity: 3}] array2 = [{name: "grape", freshness: 9}, {name: "apple", freshness: 7}, {name: "pear", freshness: 10}] 

I would like to sort array1 based on the order of array2 by name. The result will be:

 array1 = [{name: "grape", quantity: 10}, {name: "apple", quantity: 2}, {name: "pear", quantity: 3}] 
+6
source share
5 answers

Here is an easy way to do this, taking into account your current data structure.

 array1 = array1.sort_by { |x| array2.find_index { |y| y[:name] == x[:name] } } 

However, note that find_index takes O (n) time. This can be improved by using a different model for your data or by doing some preprocessing (for example, see Stefan's answer).

+5
source

You can create a hash name => index :

 h = array2.map { |e| e[:name] }.each_with_index.to_h #=> {"grape"=>0, "apple"=>1, "pear"=>2} 

And sort by this hash:

 array1.sort_by { |e| h[e[:name]] } #=> [{:name=>"grape", :quantity=>10}, {:name=>"apple", :quantity=>2}, {:name=>"pear", :quantity=>3}] 
+7
source
 h = array1.each_with_object({}){|e, h| h[e[:name]] = e} array1 = array2.map{|e| h[e[:name]]} 
+5
source
 array2.map { |h2| array1.detect { |h1| h1[:name] == h2[:name] } } 

 require 'benchmark' @array1 = [{name: "apple", quantity: 2}, {name: "grape", quantity: 10}, {name: "pear", quantity: 3}] @array2 = [{name: "grape", freshness: 9}, {name: "apple", freshness: 7}, {name: "pear", freshness: 10}] n = 500_000 Benchmark.bm do |x| x.report {n.times { @array2.map { |h2| @array1.detect { |h1| h1[:name] == h2[:name] } } } } x.report {n.times { @array1.sort_by { |x| @array2.find_index { |y| y[:name] == x[:name] } } } } x.report {n.times { h = @array1.each_with_object({}){|e, h| h[e[:name]] = e} ; @array1 = @array2.map{|e| h[e[:name]] } } } x.report {n.times { h = @array2.map { |e| e[:name] }.each_with_index.to_h ; @array1.sort_by { |e| h[e[:name]] } }} x.report {n.times { @array1.each_with_object({}) { |g,h| h[g[:name]] = g }.values_at(*@array2.map { |g| g[:name] }) }} end user system total real 0.960000 0.000000 0.960000 ( 1.064233) 1.040000 0.020000 1.060000 ( 1.291731) 0.850000 0.000000 0.850000 ( 1.064816) 1.680000 0.000000 1.680000 ( 2.131733) 0.840000 0.000000 0.840000 ( 1.057844) 

For a large array, on the other hand, @sawa and @Stefan gave equally good results (Cary solution added):

 100.times { |i| @array1 << {name:i}; @array2 << {name:i} } @array1.shuffle! @array2.shuffle! user system total real 5.970000 0.000000 5.970000 ( 6.154653) 4.980000 0.010000 4.990000 ( 5.111118) 0.450000 0.010000 0.460000 ( 0.469722) 0.640000 0.010000 0.650000 ( 0.655721) 0.480000 0.010000 0.490000 ( 0.490590) 
+2
source
 array1.each_with_object({}) { |g,h| h[g[:name]] = g }. values_at(*array2.map { |g| g[:name] }) #=> [{:name=>"grape", :quantity=>10}, {:name=>"apple", :quantity=>2}, # {:name=>"pear", :quantity=>3}] 
+1
source

All Articles