как сгруппировать числа в разные ведра в ruby - PullRequest
5 голосов
/ 29 ноября 2010

допустим, у меня есть файл с номерами в каждой строке:

0101
1010
1311
0101
1311
431
1010
431
420

В конце я получу хеш с числом вхождений каждого числа, в данном случае:

{0101 => 2, 1010 => 2, 1311 => 2, 431 => 2, 420 => 1}

как мне этого добиться?

Ответы [ 3 ]

11 голосов
/ 29 ноября 2010

Простой однострочник, заданный массив items:

items.inject(Hash.new(0)) {|hash, item| hash[item] += 1; hash}

Как это работает:

Hash.new(0) создает новый хэш, в котором доступ к неопределенным ключам возвращает 0.

inject(foo) перебирает массив с данным блоком. Для первой итерации она проходит foo, а на последующих итерациях она возвращает возвращаемое значение последней итерации.

Другой способ написать это будет:

hash = Hash.new(0)
items.each {|item| hash[item] += 1}
4 голосов
/ 09 марта 2011

По сути, это то же самое, что и у Чака, но когда вы создаете массив или хеш, 'each_with_object' сделает его немного проще, чем 'inject', поскольку вам не нужно записывать окончательный массив или хеш в блоке.

items.each_with_object(Hash.new(0)) {|item, hash| hash[item] += 1}
2 голосов
/ 29 ноября 2010
ID = -> x { x } # Why is the identity function not in the core lib?

f = <<-HERE
  0101
  1010
  1311
  0101
  1311
  431
  1010
  431
  420
HERE

Hash[f.lines.map(&:to_i).group_by(&ID).map {|n, ns| [n, ns.size] }]
# { 101 => 2, 1010 => 2, 1311 => 2, 431 => 2, 420 => 1 }

Вы просто группируете числа по отдельности, используя Enumerable#group_by, что дает вам что-то вроде

{ 101 => [101, 101], 420 => [420] }

А затем вы Enumerable#map массив значений на их длины, т. Е. [101, 101] становится 2. Затем просто конвертируйте его обратно в Hash, используя Hash::[].

Однако, если вы хотите использовать стороннюю библиотеку, она становится еще более тривиальной, поскольку, если вы используете структуру данных MultiSet, ответ естественно выпадает. (A MultiSet похож на Set, за исключением того, что элемент можно добавлять несколько раз, а MultiSet будет вести подсчет того, как часто элемент добавлялся - это именно то, что вам нужно.)

require 'multiset' # Google for it, it's so old that it isn't available as a Gem

Multiset[*f.lines.map(&:to_i)]
# => #<Multiset:#2 101, #2 1010, #2 1311, #2 431, #1 420>

Да, все.

Это замечательная вещь при использовании правильной структуры данных: ваши алгоритмы становятся намного проще. Или, в данном конкретном случае, алгоритм просто исчезает .

Я написал больше об использовании MultiSet s для решения этой точной проблемы на

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...