Подтвердить что ты не робот

Уникально на массиве хешей, основанных на значении

Я чувствую, что это может быть улучшено (общее чувство в рубине). Я пытаюсь uniq массив хэшей на основе значения. В этом примере я хочу цвета элементов. Мосс и снег - обманщики.

# remove unique array of hashes based on a hash value

a = [
  { :color => "blue", :name => "water" },
  { :color => "red", :name => "fire" },
  { :color => "white", :name => "wind" },
  { :color => "green", :name => "earth" },
  { :color => "green", :name => "moss" },
  { :color => "white", :name => "snow" }
]

# remove moss and snow
uniques = []
a.each_with_index do |r, i|
  colors = uniques.collect {|e| e[:color]}

  if !colors.include? r[:color]
    uniques.push r
  else
    a[i] = nil
  end
end

a.compact!

puts a

Откроется

{:color=>"blue", :name=>"water"}
{:color=>"red", :name=>"fire"}
{:color=>"white", :name=>"wind"}
{:color=>"green", :name=>"earth"}

Что является "правильным", однако я чувствую, что это чрезмерно. Мой опыт с .map.inject ограничен, и эти передовые методы ускользают от меня. Если кто-то может изменить это, это может помочь мне понять еще одну технику.

4b9b3361

Ответ 1

В Ruby 1.9 попробуйте выполнить

a.uniq! {|e| e[:color] }

Ответ 2

Я бы пошел с методами Array reject или select:

require 'pp'

a = [
  { :color => "blue", :name => "water" },
  { :color => "red", :name => "fire" },
  { :color => "white", :name => "wind" },
  { :color => "green", :name => "earth" },
  { :color => "green", :name => "moss" },
  { :color => "white", :name => "snow" }
]

pp a.reject{ |h| %w[moss snow].include?( h[:name]) } 
# >> [{:color=>"blue", :name=>"water"},
# >>  {:color=>"red", :name=>"fire"},
# >>  {:color=>"white", :name=>"wind"},
# >>  {:color=>"green", :name=>"earth"}]

В качестве альтернативы вы можете быть уверены в этом, а select те, которые вы хотите сохранить:

pp a.select{ |h| %w[water fire wind earth].include?( h[:name] ) } 
# >> [{:color=>"blue", :name=>"water"},
# >>  {:color=>"red", :name=>"fire"},
# >>  {:color=>"white", :name=>"wind"},
# >>  {:color=>"green", :name=>"earth"}]

На самом деле вы не имеете дело с хэшами, это массив, который содержит хеши, поэтому не позволяйте им путать вас. Методы массива, такие как reject и select, являются основными методами фильтрации нежелательных или сохраняемых элементов.

В вашем примере кода вы теряете из виду свою цель: вам нужны элементы, отбрасывающие "мох" и "снег", которые не являются элементами. Отфильтруйте неэлементы, и вы останетесь с правильными/реальными элементами хэшей. Оттуда вы можете извлечь правильные цвета.

Еще одна проблема, с которой нужно следить за использованием uniq, является позиционным, другими словами, она ищет первое уникальное значение и отклоняет последующие. Это не было очевидно в вашем коде, потому что ваш массив был последовательно того же порядка, что и вы тестировали. Если вы перетасовали заказ, хотя...:

2.times do
  pp a.shuffle.uniq{ |h| h[:color] }
end

Pass # 1...

# [{:color=>"red", :name=>"fire"},
#  {:color=>"white", :name=>"wind"},
#  {:color=>"green", :name=>"moss"},
#  {:color=>"blue", :name=>"water"}]

Pass # 2...

# [{:color=>"green", :name=>"earth"},
#  {:color=>"blue", :name=>"water"},
#  {:color=>"red", :name=>"fire"},
#  {:color=>"white", :name=>"snow"}]

Вдруг мы видим, что и "мох", и "снег" проникают в результаты, хотя цвета уникальны. Это тонкая добыча, на которую вы должны следить.