2015-09-09 2 views
6

У меня есть два массива. У них разные атрибуты.Сравнение и сортировка массива хэшей в Ruby

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}] 

Я хотел бы сортировать array1 на основе порядка array2 «s, по имени. Результат будет выглядеть так:

array1 = [{name: "grape", quantity: 10}, {name: "apple", quantity: 2}, {name: "pear", quantity: 3}] 
+0

Нет, я не могу. У него уже есть ответ. – user3591126

+1

Являются ли имена уникальными и идентичными в обоих массивах? – Stefan

+0

Быстрый поиск по ["\ [ruby \] сортировке массива на основе другого массива"] (http://stackoverflow.com/search?q=%5Bruby%5D+sort+array+based+on+another+array) найдут другие варианты этого вопроса. –

ответ

5

Вот простой способ сделать это с учетом текущей структуры данных.

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

Тем не менее, отметим, что find_index занимает O (N) времени. Это можно улучшить, используя другую модель для ваших данных или сделав некоторую предварительную обработку (например, см. Ответ Стефана).

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

Сортировка без использования 'sort', мне потребовалось некоторое время, чтобы понять, что происходит :-) – Stefan

+0

@Stefan Я считал модификацию 'array1' в соответствующей структуре. Вы считали модификацию 'array2'. С точки зрения, что вопрос заключается в переупорядочении 'array1', ваш ответ может иметь больше смысла. – sawa

7

Вы могли бы построить name => index хэш:

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

И сортировать по этой хэш:

array1.sort_by { |e| h[e[:name]] } 
#=> [{:name=>"grape", :quantity=>10}, {:name=>"apple", :quantity=>2}, {:name=>"pear", :quantity=>3}] 
+0

Benchmarks говорят, что этого недостаточно. :) – mudasobwa

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) 

Для большого массива, с другой стороны, @ sawa и @Stefan дали одинаково хорошие результаты (добавили соло Кэри ион):

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) 
+0

Попробуйте сравнить большие массивы: '100.times {| i | @ array1 << {name: i}; @ array2 << {name: i}} ' – Stefan

+0

Получил это. Обновленные тесты. Благодарю. – mudasobwa

+0

Можете ли вы добавить тот, который я только что написал? Он выглядит относительно быстрым для больших массивов. –

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}] 
Смежные вопросы