2014-08-29 3 views
2

У меня есть следующая логика:Какой самый идиоматический способ выполнить несколько тестов в рубине?

some_array.each do |element| 
    if element[:apples] == another_hash[:apples] && 
    element[:oranges] == another_hash[:oranges] && 
    element[:pineapple] == another_hash[:pineapple] 
     match = element 
    break 
    end 
end 

Я перебирать список пар ключ-значение. Если я могу сопоставить требуемые ключи (3 из 5), то я брошу элемент в var для последующего использования. Если я нахожу совпадение, я выхожу из цикла.

Я ищу самый идиоматический способ оптимизации этого условного. Заранее спасибо.

ответ

4

Как насчет:

match = some_array.find do |element| 
    [:apples, :oranges, :pinapple].all? {|key| element[key] == another_hash[key]} 
end 

Если вы хотите, чтобы выбрать какой-либо элемент, который имеет, по меньшей мере, 3 одинаковых ключей от 5 ключей, данные затем:

match = some_array.find do |element| 
    element.keys.select {|key| element[key| == another_hash[key]}.size > 2 
end 
+0

Freakin awesome. Гораздо лучше, чем раньше. Спасибо! – user2152283

+0

Для моего примера, с 'fruit = [: apple,: plum]' вы возвращаете '{: apple => 6,: cherry => 2,: pineapple => 8,: fig => 3}', но 'another_hash 'не имеет ключа': plum', поэтому он должен возвращать 'nil'. –

+0

@CarySwoveland - Оба хэша не имеют ': plum', поэтому он работает так, как я полагаю, я верю? :) – BroiSatse

1

Более читаемый вариант я мог думать, является slice:

keys = [:apples, :oranges, :pinapple] 
match = some_array.find {|e| e.slice(*keys) == another_hash.slice(*keys)} 

UPDATE

Кусочек не является чистым рубиновым методом Hash, он включает в себя библиотеку ActiveSupport Rails.

Если вы не хотите использовать Rails, вы можете просто загрузить Active Support.

Добавить active_support в ваш Gemfile и require "active_support/core_ext/hash/slice".

Или вы можете просто вставить содержимое slice.rb в свое приложение где-нибудь. URL-адрес можно найти: here.

+0

Это похоже на отличное решение. Спасибо за ваш отзыв! – user2152283

+0

Кошка, вы имели в виду 'values_at', где у вас' slice'? Ни Hash', ни 'Enumerable' не имеют метода' slice'. С 'values_at', это почти то, что у меня есть, за исключением строки для решения проблемы, о которой я упоминал в комментарии к решению @ BroiSatse. –

+0

@ CarySwoveland Спасибо за ваше предложение. Я обновил свой ответ. –

3

Вот как я это сделаю.

Код

def fruit_match(some_array, another_hash, fruit) 
    other_vals = another_hash.values_at(*fruit) 
    return nil if other_vals.include?(nil) 
    some_array.find { |h| h.values_at(*fruit) == other_vals } 
end 

Примеры

some_array = [ { apple: 1, orange: 2, pineapple: 3, plum: 4 }, 
       { apple: 1, cherry: 7, pineapple: 6, plum: 2 }, 
       { apple: 6, cherry: 2, pineapple: 8, fig: 3 } ] 

another_hash = { apple: 6, cherry: 4, pineapple: 8, quamquat: 5 } 

fruit = [:apple, :pineapple] 
fruit_match(some_array, another_hash, fruit) 
    #=> { :apple=>6, :cherry=>2, :pineapple=>8, :fig=>3 } 

fruit = [:apple, :plum] 
fruit_match(some_array, another_hash, fruit) 
    #=> nil 

[Edit: я не заметил "3-5" спичками, пока я не увидел @ ответ 7stud в. Требование количества совпадений, попадающих в заданный диапазон, является интересным вариантом. Вот как я бы рассмотрел это требование.

код

def fruit_match(some_array, another_hash, fruit, limits) 
    other_vals = another_hash.values_at(*fruit) 
    some_array.select { |h| limits.cover?(h.values_at(*fruit) 
           .zip(other_vals) 
           .count {|e,o| e==o && e}) } 
end 

Пример

some_array = [ { apple: 1, orange: 2, pineapple: 1, cherry: 1 }, 
       { apple: 2, cherry: 7, pineapple: 6, plum: 2 }, 
       { apple: 6, cherry: 1, pineapple: 8, fig: 3 }, 
       { apple: 1, banana: 2, pineapple: 1, fig: 3 } ] 

another_hash = { apple: 1, cherry: 1, pineapple: 1, quamquat: 1 } 
fruit = [:apple, :pineapple, :cherry] 
limits = (1..2) 

fruit_match(some_array, another_hash, fruit, limits) 
    #=> [{:apple=>6, :cherry=>1, :pineapple=>8, :fig=>3}, 
    # {:apple=>1, :banana=>2, :pineapple=>1, :fig=>3}] 

Tide]

+0

Вы использовали '# values_at' .. Я пытался дать такое же решение .. Я бы не стал .. +1 .. :-) –

+0

Спасибо Кэри за отзыв. Это также действительное решение для меня. У меня есть варианты использования для всех трех ответов здесь, поэтому это определенно наиболее полезно. Спасибо! – user2152283

+0

** просто замените find с помощью select ** Я не вижу, как это будет работать. – 7stud

1

Если я может соответствовать требуемым ключи (3 из 5)

Я не думаю, что любой из опубликованных ответов адресует это.

target_keys = %i[ 
    apples 
    oranges 
    pineapples 
    strawberries 
    bananas 
] 

data = [ 
    {beer: 0, apples: 1, oranges: 2, pineapples: 3, strawberries: 4, bananas: 5}, 
    {beer: 1, apples: 6, oranges: 7, pineapples: 8, strawberries: 9, bananas: 10}, 
    {beer: 2, apples: 6, oranges: 2, pineapples: 3, strawberries: 9, bananas: 10}, 
] 

match_hash = { 
    apples: 6, oranges: 2, pineapples: 3, strawberries: 9, bananas: 10 
} 

required_matches = 3 
required_values = match_hash.values_at(*target_keys).to_enum 
found_match = nil 

catch :done do 

    data.each do |hash| 
    found_values = hash.values_at(*target_keys).to_enum 
    match_count = 0 

    loop do 
     match_count += 1 if found_values.next == required_values.next 

     if match_count == required_matches 
     found_match = hash 
     throw :done 
     end 
    end 

    required_values.rewind 
    end 

end 

p found_match 

--output:-- 
{:beer=>1, :apples=>6, :oranges=>7, :pineapple=>8, :strawberry=>9, :banana=>10 
+0

Спасибо, 7stud. Этот ответ также дал мне некоторые идеи. Я ценю вас за то, что вы нашли время. – user2152283

+0

@ user2152283, я немного пересмотрел свой ответ. – 7stud

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