2012-05-31 6 views
1

Я нахожу, что постоянно хочу посчитать количество вхождений элемента в массиве и отобразить верхние элементы вместе с их фактическим количеством. Я написал код, как следующие, так я много раз распознаю его как повторяющаяся, РСИТЕ-индуцирующую схему:Элементы сортировки и отображения по количеству вхождений

hits = Hash[ array.group_by{|o|o}.map{|o,a|[o,a.length]}.sort_by{|o,ct|[-ct,o]} ] 

require 'pp' 
pp hits 

Я мог пошевелиться это в monkeypatch на перечислимом ...

module Enumerable 
    def counts(&blk) 
    blk ||= ->(o){o} 
    Hash[ group_by(&blk).map{|o,a| [o,a.length] }.sort_by{|o,ct| [-ct,o] } ] 
    end 
end 

a = %w[a b a b c d e g j a e c d k o k i l p a e c f d e a d e f s d v c ] 
pp a.counts 
#=> {"a"=>5, 
#=> "d"=>5, 
#=> "e"=>5, 
#=> "c"=>4, 
#=> "b"=>2, 
#=> "f"=>2, 
#=> "k"=>2, 
#=> "g"=>1, 
#=> "i"=>1, 
#=> "j"=>1, 
#=> "l"=>1, 
#=> "o"=>1, 
#=> "p"=>1, 
#=> "s"=>1, 
#=> "v"=>1} 

... но интересно если есть более элегантный способ сделать это (меньше типизации будет достаточно), используя основные методы Ruby.

+1

Он выглядит хорошо для меня, хотя с помощью Hash.new (0) + инъекции (или each_with_object) будет более эффективным, чем 'group_by'. Обратите внимание, что уже есть довольно много вопросов, которые отвечают на «частоту элементов в массиве», показывающую как group_by, так и инъекционные решения. – tokland

+0

@tokland Я буду счастливо закрыть этот ответ для дубликата (я его не нашел) или принять ответ, показывающий это, если результат имеет хеш-ключи или парный массив, отсортированные по нисходящим ударам. – Phrogz

+0

Phrogz, я нашел ваши недавние вопросы о Ruby интересными (особенно те, которые связаны с комбинаторикой), поэтому посмотрим на некоторые из ваших старых вопросов. Вероятно, я буду публиковать комментарии и решения от времени до времени. –

ответ

1

Похоже, что код, который у меня есть, примерно такой же короткий, как и он. Переехал в метод, не monkeypatching:

def count_items(enum,&blk) 
    blk ||= ->(o){o} 
    Hash[ enum.group_by(&blk).map{|o,a| [o,a.length] }.sort_by{|o,ct| [-ct,o] } ] 
end 
5
ruby-1.9.2-p290 :041 > Hash[*[1,1,2,3,4,5,5,5].inject(Hash.new(0)) { |h,v| h[v] += 1; h }.sort_by{|k,v| v}.reverse.flatten] 

=> {5=>3, 1=>2, 4=>1, 2=>1, 3=>1} 

Слушайте, если вы хотите, чтобы ранжировать список не основан на нет вхождений, следующие работы хорошо,

ruby-1.9.2-p290 :045 > [1,1,2,3,4,5,5,5].group_by{|x| x}.sort_by{|k, v| -v.size}.map(&:first) 

=> [5, 1, 2, 4, 3] 
+0

Обратите внимание, что под Ruby 1.8.7+ вы можете просто использовать 'Hash [array_of_tuples]' вместо 'Hash [* array_of_tuples.flatten]' – Phrogz

+0

Спасибо человеку. Я это заметлю. – beck03076

0

Без сортировки заранее, что может быть медленным для высокой п:

a = %w[a b a b c d e g j a e c d k o k i l p a e c f d e a d e f s d v c ] 
a.each_with_object({}) {|e, h| h[e] ||= 0; h[e] += 1 }.sort_by {|o, ct| [-ct, o] } 

Если вы заботитесь об элегантности, используйте #with_object/#each_with_object, когда это подходит.

Смежные вопросы