Se houver dois elementos máximos em uma matriz?
Neste código se o usuário digitar 2, duas vezes e 1, duas vezes. Então, há dois elementos máximos e ambos Kinder e Twix devem ser impressos. Mas como ? Provavelmente posso fazer isso com o método if, mas isso tornará meu código ainda mais longo. Alguma versão legal? Posso fazer isso com apenas um if?
a = [0, 0, 0,]
b = ["Kinder", "Twix", "Mars"]
while true
input = gets.chomp.to_i
if input == 1
a[0] += 1
elsif input == 2
a[1] += 1
elsif input == 3
a[2] += 1
elsif input == 0
break
end
end
index = a.index(a.max)
chocolate = b[index] if index
print a.max,chocolate
Respostas
A questão realmente não tem nada a ver com como o array a
é construído.
def select_all_max(a, b)
mx = a.max
b.values_at(*a.each_index.select { |i| a[i] == mx })
end
b = ["Kinder", "Twix", "Mars"]
p select_all_max [0, 2, 1], b
["Twix"]
p select_all_max [2, 2, 1], b
["Kinder", "Twix"]
Consulte Array # values_at .
Isso também pode ser feito em uma única passagem.
def select_all_max(a, b)
b.values_at(
*(1..a.size-1).each_with_object([0]) do |i,arr|
case a[i] <=> arr.last
when 0
arr << i
when 1
arr = [i]
end
end
)
end
p select_all_max [0, 2, 1], b
["Twix"]
p select_all_max [2, 2, 1], b
["Kinder", "Twix"]
p select_all_max [1, 1, 1], b
["Kinder", "Twix", "Mars"]
Uma maneira seria a seguinte:
Primeiro, basta separar a coleta de dados da contagem, então vamos apenas reunir os dados nesta etapa:
inputs = []
loop do
input = gets.chomp.to_i
break if input.zero?
inputs << input
end
Agora podemos registrar as entradas. Se você tem Ruby 2.7, pode simplesmente fazer counts_by_input = inputs.tally
para obter { "Twix" => 2, "Kinder" => 2 }
. Caso contrário, minha abordagem preferida é usar group_by com transform_values :
counts_by_input = inputs.group_by(&:itself).transform_values(&:count)
# => { "Twix" => 2, "Kinder" => 2 }
Agora, como iremos extrair valores com base em sua contagem, queremos ter as contagens como chaves. Normalmente podemos inverter o hash, mas isso não funcionará neste caso porque nos dará apenas um valor por chave, e precisamos de vários:
inputs_by_count = counts_by_input.invert
# => { 2 => "Kinder" }
# This doesn't work, it removed one of the values
Em vez disso, podemos usar outro group_by
e transform_values
(o motivo pelo qual gosto desses métodos é porque eles são muito versáteis ...):
inputs_by_count = counts_by_input.
group_by { |input, count| count }.
transform_values { |keyvals| keyvals.map(&:first) }
# => { 2 => ["Twix", "Kinder"] }
O transform_values
código aqui provavelmente é um pouco confuso, mas é importante entender que, muitas vezes, chamar métodos Enumerable em hashes os converte em [[key1, val1], [key2, val2]]
arrays:
counts_by_input.group_by { |input, count| count }
# => { 2 => [["Twix", 2], ["Kinder", 2]] }
É por isso que ligamos transform_values { |keyvals| keyvals.map(&:first) }
depois para obter o formato desejado{ 2 => ["Twix", "Kinder"] }
De qualquer forma, neste ponto obter nosso resultado é muito fácil:
inputs_by_count[inputs_by_count.keys.max]
# => ["Twix", "Kinder"]
Eu sei que isso provavelmente parece um pouco insano, mas quando você se familiarizar com os métodos do Enumerable, será capaz de fazer esse tipo de transformação de dados com bastante fluência.
Tl; dr, dê-me o código
inputs = []
loop do
input = gets.chomp.to_i
break if input.zero?
inputs << input
end
inputs_by_count = inputs.
group_by(&:itself).
transform_values(&:count).
group_by { |keyvals, count| count }.
transform_values { |keyvals| keyvals.map(&:first) }
top_count = inputs_by_count.keys.max
inputs_by_count[top_count]
# => ["Twix", "Kinder"]
Que tal algo como isso:
maximum = a.max # => 2
top_selling_bars = a.map.with_index { |e, i| b[i] if e == maximum }.compact # => ['Kinder', 'Twix']
p top_selling_bars # => ['Kinder', 'Twix']
Se você tem
a = [2, 2, 0,]
b = ['Kinder', 'Twix', 'Mars']
Você pode calcular o valor máximo em a
:
max = a.max #=> 2
e encontre todos os elementos correspondentes a esse valor por meio de:
b.select.with_index { |_, i| a[i] == max }
#=> ["Kinder", "Twix"]