2016-07-21 4 views
2

У меня есть два хэша hash_a и hash_b, которые на самом деле являются массивами, но имеют хэш внутри них. Эти хэш имеют уникальный ключ.Самый эффективный способ сравнения массивов с хэшами внутри них на основе уникального хэш-ключа в Ruby

hash_a = [ 
{:unique_key => 1, :data => 'data for A1'}, 
{:unique_key => 2, :data => 'data for A2'}, 
{:unique_key => 3, :data => 'data for A3'} 
] 

hash_b = [ 
{:unique_key => 1, :data => 'data for B1'}, 
{:unique_key => 2, :data => 'data for B2'}, 
{:unique_key => 4, :data => 'data for B4'}, 
{:unique_key => 5, :data => 'data for B5'} 
] 

Теперь я хочу, чтобы узнать разницу между hash_a и hash_b, так что я получаю hash_c как массив новых хэшей, присутствующих в hash_b. Я в принципе хочу hash_b - hash_a

Так что я хочу, это выход для hash_c, hash_c должен быть таким:

[ 
{:unique_key => 1, :data => 'data for A1'}, 
{:unique_key => 2, :data => 'data for A2'}, 
{:unique_key => 3, :data => 'data for A3'}, 
{:unique_key => 4, :data => 'data for B4'}, 
{:unique_key => 5, :data => 'data for B5'} 
] 

Я пытался что-то вроде этого:

hash_c = hash_a 
hash_b.each do |inner_bhash| 
    found = 0 

    hash_a.each do |inner_ahash| 
     if(inner_ahash[:unique_key] == inner_bhash[:unique_key]) 
      found = 1 
      break 
     end 
    end 

    if(found==0) 
     hash_c.push(inner_bhash) 
    end 
end 

Это делает трюк, но Я хочу лучше. Как hashmap или что-то, я не знаю, что.


Кроме того, возможно, я хочу видеть только новые записи, т.е.

[ 
    {:unique_key => 4, :data => 'data for B4'}, 
    {:unique_key => 5, :data => 'data for B5'} 
] 

я могу сделать это в моем коде, заменив

hash_c = hash_a 

с

hash_c = [] 

, но как я могу адаптировать это требование в так же?

ответ

3

С Hash эс вы можете использовать merge делать то, что вы хотите - так происходит через делая каждый Array в Hash вы можете сделать следующее:

hash_b.group_by { |e| e[:unique_key] }. 
    merge(hash_a.group_by { |e| e[:unique_key] }).values.flatten 
# => [{:unique_key=>1, :data=>"data for A1"}, 
#  {:unique_key=>2, :data=>"data for A2"}, 
#  {:unique_key=>4, :data=>"data for B4"}, 
#  {:unique_key=>5, :data=>"data for B5"}, 
#  {:unique_key=>3, :data=>"data for A3"}] 

Если вы хотите иметь только записи от hash_b (у которых нет ключа в hash_a), учитывая, что у вас уже есть решение выше - вы можете просто вычестьhash_a из результата:

hash_b.group_by { |e| e[:unique_key] }. 
    merge(hash_a.group_by { |e| e[:unique_key] }).values.flatten - hash_a 
# => [{:unique_key=>4, :data=>"data for B4"}, 
#  {:unique_key=>5, :data=>"data for B5"}] 

Другой, более прямой путь, чтобы отфильтровать все элементы, которые имеют hash_b запись в hash_a:

hash_b.select { |x| hash_a.none? { |y| x[:unique_key] == y[:unique_key] } } 
# => [{:unique_key=>4, :data=>"data for B4"}, 
#  {:unique_key=>5, :data=>"data for B5"}] 
+0

чистая эпичность .. – marcusshep

+0

Привет, спасибо большое, вы можете увидеть мой обновленный вопрос, я сожалею об обновлении вопроса, но я также хочу знать, как можно получить только новые значения hash_b? – user1735921

+0

Вы все значения в hash_b, у которых нет записей с тем же ключом на hash_a? –

1

Вы можете использовать форму Array#uniq, который принимает блок.

(hash_a + hash_b).uniq { |h| h[:unique_key] } 
    #=> [{:unique_key=>1, :data=>"data for A1"}, {:unique_key=>2, :data=>"data for A2"}, 
    # {:unique_key=>3, :data=>"data for A3"}, {:unique_key=>4, :data=>"data for B4"}, 
    # {:unique_key=>5, :data=>"data for B5"}] 

Чтобы процитировать документ, «само происходит по порядку, и первое вхождение сохраняется».

 Смежные вопросы

  • Нет связанных вопросов^_^