6

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 array2's order, by name. The result would be:

array1 = [{name: "grape", quantity: 10}, {name: "apple", quantity: 2}, {name: "pear", quantity: 3}]
4
  • No. I can't. It already has an answer. Commented Sep 9, 2015 at 16:33
  • 1
    Are the names unique and identical in both arrays? Commented Sep 9, 2015 at 16:41
  • A quick search on "[ruby] sort array based on another array" will find other versions of this question. Commented Sep 9, 2015 at 16:45
  • 1
    @muistooshort I've reopened the question - I think comparing / sorting by identical attributes is a special case and there might be more specific answers to this question Commented Sep 9, 2015 at 16:49

5 Answers 5

7

You could build a name => index hash:

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

And sort by that hash:

array1.sort_by { |e| h[e[:name]] }
#=> [{:name=>"grape", :quantity=>10}, {:name=>"apple", :quantity=>2}, {:name=>"pear", :quantity=>3}]
Sign up to request clarification or add additional context in comments.

Comments

5

Here's a simple way to do it given 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 (e.g., see Stefan's answer).

Comments

5
h = array1.each_with_object({}){|e, h| h[e[:name]] = e}
array1 = array2.map{|e| h[e[:name]]}

2 Comments

Sorting without using sort, it took me a while to understand what's going on :-)
@Stefan I considered modifying array1 into an appropriate structure. You considered modifying array2. From the point of view that the question is to reorder array1, your answer may make more sense.
2
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 large array, on the other hand, @sawa and @Stefan gave equally good results (added a Cary's solution):

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)

3 Comments

Try to benchmark larger arrays: 100.times { |i| @array1 << {name:i}; @array2 << {name:i} }
Can you add the one I just posted? It appears relatively fast for larger arrays.
@CarySwoveland yours appears to be a winner amongst Sawa's: it is in fastest group for both small and huge arrays.
1
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}] 

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.