2015-09-09 20 views
6

Ho due array. Hanno attributi diversi.Confronto e ordinamento array di hash in 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}] 

Vorrei ordinare array1 sulla base di ordine array2 s', in base al nome. Il risultato sarebbe:

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

No. Non posso. Ha già una risposta. – user3591126

+1

I nomi sono univoci e identici in entrambi gli array? – Stefan

+0

Una ricerca rapida su ["\ [ruby \] ordina array basato su un altro array"] (http://stackoverflow.com/search?q=%5Bruby%5D+sort+array+based+on+un altro+array) troverà altre versioni di questa domanda. –

risposta

5

Ecco un modo semplice per farlo dato la struttura dati corrente.

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

Tuttavia, si noti che find_index prende O (n). Questo può essere migliorato utilizzando un modello diverso per i tuoi dati o eseguendo un po 'di pre-elaborazione (ad esempio, vedi la risposta di Stefan).

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

Ordinamento senza usare 'sort', mi ci è voluto un po 'per capire cosa sta succedendo :-) – Stefan

+0

@Stefan Ho considerato la modifica di 'array1' in una struttura appropriata. Hai preso in considerazione la modifica di 'array2'. Dal punto di vista che la domanda è di riordinare 'array1', la tua risposta potrebbe avere più senso. – sawa

7

Si potrebbe costruire un name => index hash:

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

E ordina per tale hash:

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

I benchmark dicono che questo non è abbastanza buono :) – 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) 

Per vasta gamma, d'altra parte, @ sawa e @Stefan hanno dato altrettanto buoni risultati (ha aggiunto il solut di Cary ion):

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

Provare a confrontare array più grandi: '100.times {| i | @ array1 << {nome: i}; @ array2 << {nome: i}} ' – Stefan

+0

Capito. Benchmark aggiornati. Grazie. – mudasobwa

+0

Puoi aggiungere quello che ho appena postato? Sembra relativamente veloce per gli array più grandi. –

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