У меня есть этот массив, например (размер является переменным):
x = ["1.111", "1.122", "1.250", "1.111"]
и я должен найти самое общее значение ("1.111"
в этом случае).
Существует ли простой способ сделать это?
Спасибо заранее!
РЕДАКТИРОВАНИЕ № 1: Спасибо всем за ответы!
РЕДАКТИРОВАНИЕ № 2: я изменил свой принятый ответ на основе информации о Z.E.D.'s. Поблагодарите всех снова!
#!/usr/bin/ruby1.8
def most_common_value(a)
a.group_by do |e|
e
end.values.max_by(&:size).first
end
x = ["1.111", "1.122", "1.250", "1.111"]
p most_common_value(x) # => "1.111"
Примечание: Enumberable.max_by
является новым в Ruby 1.9, но был перенесен на 1.8.7
Ruby 2.2 представляет метод Object # , с помощью которого мы можем сделать код более кратким:
def most_common_value(a)
a.group_by(&:itself).values.max_by(&:size).first
end
Или как Enumerable # mode
:
Enumerable.class_eval do
def mode
group_by do |e|
e
end.values.max_by(&:size).first
end
end
["1.111", "1.122", "1.250", "1.111"].mode
# => "1.111"
Один проход через хэш для накопления счетчиков. Используйте .max (), чтобы найти хеш-запись с наибольшим значением.
#!/usr/bin/ruby a = Hash.new(0) ["1.111", "1.122", "1.250", "1.111"].each { |num| a[num] += 1 } a.max{ |a,b| a[1] <=> b[1] } # => ["1.111", 2]
или свести все в одну строку:
ary.inject(Hash.new(0)){ |h,i| h[i] += 1; h }.max{ |a,b| a[1] <=> b[1] } # => ["1.111", 2]
Если вы хотите вернуть только элемент, добавьте .first ():
ary.inject(Hash.new(0)){ |h,i| h[i] += 1; h }.max{ |a,b| a[1] <=> b[1] }.first # => "1.111"
Первый пример, который я использовал, показывает, как это обычно делается в Perl. Второй - более рубиновый. Оба работают со старыми версиями Ruby. Я хотел сравнить их, а также посмотреть, как решение Уэйна ускорит процесс, поэтому я протестировал с помощью теста:
#!/usr/bin/env ruby require 'benchmark' ary = ["1.111", "1.122", "1.250", "1.111"] * 1000 def most_common_value(a) a.group_by { |e| e }.values.max_by { |values| values.size }.first end n = 1000 Benchmark.bm(20) do |x| x.report("Hash.new(0)") do n.times do a = Hash.new(0) ary.each { |num| a[num] += 1 } a.max{ |a,b| a[1] <=> b[1] }.first end end x.report("inject:") do n.times do ary.inject(Hash.new(0)){ |h,i| h[i] += 1; h }.max{ |a,b| a[1] <=> b[1] }.first end end x.report("most_common_value():") do n.times do most_common_value(ary) end end end
Вот результаты:
user system total real Hash.new(0) 2.150000 0.000000 2.150000 ( 2.164180) inject: 2.440000 0.010000 2.450000 ( 2.451466) most_common_value(): 1.080000 0.000000 1.080000 ( 1.089784)
Вы можете отсортировать массив, а затем перебрать его один раз. В цикле просто отслеживайте текущий элемент и количество его просмотров. Когда список закончится или элемент изменится, установите max_count == count
, если count> max_count
. И, конечно же, отслеживайте, какой элемент имеет max_count
.
Вы можете создать хэш-карту, в которой элементы массива будут храниться в виде ключей, причем их значения будут указывать, сколько раз элемент появляется в массиве.
Псевдокод:
["1.111", "1.122", "1.250", "1.111"].each { |num|
count=your_hash_map.get(num)
if(item==nil)
hashmap.put(num,1)
else
hashmap.put(num,count+1)
}
Как уже упоминалось, сортировка может быть быстрее.
Использование функции значений хэшей по умолчанию:
>> x = ["1.111", "1.122", "1.250", "1.111"]
>> h = Hash.new(0)
>> x.each{|i| h[i] += 1 }
>> h.max{|a,b| a[1] <=> b[1] }
["1.111", 2]
Он вернет самое популярное значение в массиве
x.group_by{|a| a }.sort_by{|a,b| b.size<=>a.size}.first[0]
IE:
x = ["1.111", "1.122", "1.250", "1.111"]
# Most popular
x.group_by{|a| a }.sort_by{|a,b| b.size<=>a.size}.first[0]
#=> "1.111
# How many times
x.group_by{|a| a }.sort_by{|a,b| b.size<=>a.size}.first[1].size
#=> 2