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

Как группировать по счету в массиве без использования цикла

arr = [1,2,1,3,5,2,4]

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

x[1] = 2  
x[2] = 2  
x[3] = 1  
x[4] = 1  
x[5] = 1
4b9b3361

Ответ 1

x = arr.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }

Ответ 2

Доступно только под 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

Ответ 3

x = Hash[arr.uniq.map{ |i| [i, arr.count(i)] }]

Ответ 4

Всякий раз, когда вы обнаружите, что кто-то утверждает, что что-то является самым быстрым в этом типе примитивной рутины, мне всегда кажется интересным подтвердить, что, поскольку без подтверждения большинство из нас действительно просто догадываются. Поэтому я использовал все методы и сравнил их.

Я взял массив из 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}

Ответ 5

Еще один - аналогичный другим - подход:

result=Hash[arr.group_by{|x|x}.map{|k,v| [k,v.size]}]
  • Группировать по каждому значению элемента.
  • Сопоставьте группировку с массивом пар [value, counter].
  • Поверните массив paris в значения ключа внутри хеша, т.е. через result[1]=2 ....

Ответ 6

Я уверен, что есть лучшие способы,

>> arr.sort.group_by {|x|x}.each{|x,y| print "#{x} #{y.size}\n"}
1 2
2 2
3 1
4 1
5 1

присваивать значения х и у хешу по мере необходимости.

Ответ 7

Это должно сделать это

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}

Ответ 8

Только для записи я недавно прочитал о Object#tap здесь. Мое решение было бы:

Hash.new(0).tap{|h| arr.each{|i| h[i] += 1}}

Метод #tap передает вызывающего абонента в блок, а затем возвращает его. Это очень удобно, когда вам нужно постепенно наращивать массив/хэш.

Ответ 9

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}