arr = [1,2,1,3,5,2,4]
Как я могу подсчитать массив по значению группы при сортировке? Мне нужен следующий вывод:
x[1] = 2
x[2] = 2
x[3] = 1
x[4] = 1
x[5] = 1
arr = [1,2,1,3,5,2,4]
Как я могу подсчитать массив по значению группы при сортировке? Мне нужен следующий вывод:
x[1] = 2
x[2] = 2
x[3] = 1
x[4] = 1
x[5] = 1
x = arr.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }
Доступно только под ruby 1.9
В основном то же самое, что Майкл отвечает, но немного короче:
x = arr.each_with_object(Hash.new(0)) {|e, h| h[e] += 1}
В подобных ситуациях
Array
, Hash
, String
, вы можете использовать each_with_object
, как в случае выше.Когда начальный элемент является неизменным объектом, таким как Numeric
, вы должны использовать inject
, как показано ниже.
sum = (1..10).inject(0) {|sum, n| sum + n} # => 55
x = Hash[arr.uniq.map{ |i| [i, arr.count(i)] }]
Всякий раз, когда вы обнаружите, что кто-то утверждает, что что-то является самым быстрым в этом типе примитивной рутины, мне всегда кажется интересным подтвердить, что, поскольку без подтверждения большинство из нас действительно просто догадываются. Поэтому я использовал все методы и сравнил их.
Я взял массив из 120 ссылок, которые я извлек из веб-страницы, которую мне нужно было группировать по счету, и реализовал все это, используя цикл секунд = Benchmark.realtime do и получил все время.
Предположим, что ссылки - это имя массива, которое мне нужно подсчитать:
#0.00077
seconds = Benchmark.realtime do
counted_links = {}
links.each { |e| counted_links[e] = links.count(e) if counted_links[e].nil?}
end
seconds
#0.000232
seconds = Benchmark.realtime do
counted_links = {}
links.sort.group_by {|x|x}.each{|x,y| counted_links[x] = y.size}
end
#0.00076
seconds = Benchmark.realtime do
Hash[links.uniq.map{ |i| [i, links.count(i)] }]
end
#0.000107
seconds = Benchmark.realtime do
links.inject(Hash.new(0)) {|h, v| h[v] += 1; h}
end
#0.000109
seconds = Benchmark.realtime do
links.each_with_object(Hash.new(0)) {|e, h| h[e] += 1}
end
#0.000143
seconds = Benchmark.realtime do
links.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }
end
А потом немного рубина, чтобы выяснить ответ:
times = [0.00077, 0.000232, 0.00076, 0.000107, 0.000109, 0.000143].min
==> 0.000107
Таким образом, самый быстрый способ, ymmv, конечно же:
links.inject(Hash.new(0)) {|h, v| h[v] += 1; h}
Еще один - аналогичный другим - подход:
result=Hash[arr.group_by{|x|x}.map{|k,v| [k,v.size]}]
result[1]=2 ...
.Я уверен, что есть лучшие способы,
>> arr.sort.group_by {|x|x}.each{|x,y| print "#{x} #{y.size}\n"}
1 2
2 2
3 1
4 1
5 1
присваивать значения х и у хешу по мере необходимости.
Это должно сделать это
arr = [1,2,1,3,5,2,4]
puts arr.inject(Hash.new(0)) {|h, v| h[v] += 1; h}
#=> {1=>2, 2=>2, 3=>1, 5=>1, 4=>1}
Только для записи я недавно прочитал о Object#tap
здесь. Мое решение было бы:
Hash.new(0).tap{|h| arr.each{|i| h[i] += 1}}
Метод #tap
передает вызывающего абонента в блок, а затем возвращает его. Это очень удобно, когда вам нужно постепенно наращивать массив/хэш.
arr = [1,2,1,3,5,2,4]
r = {}
arr.each { |e| r[e] = arr.count(e) if r[e].nil?}
Выходы
p r
#==> {1=>2, 2=>2, 3=>1, 5=>1, 4=>1}