Как посчитать одинаковые строковые элементы в массиве Ruby - PullRequest
67 голосов
/ 26 февраля 2011

У меня есть следующее Array = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

Как мне рассчитать счет для каждого идентичного элемента ?

Where:
"Jason" = 2, "Judah" = 3, "Allison" = 1, "Teresa" = 1, "Michelle" = 1?

или создать хеш Где:

Где: hash = {"Jason" => 2, "Judah" => 3, "Allison" => 1, "Тереза" => 1, "Мишель" => 1}

Ответы [ 14 ]

110 голосов
/ 26 февраля 2011
names.inject(Hash.new(0)) { |total, e| total[e] += 1 ;total}

дает вам

{"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1} 
69 голосов
/ 26 февраля 2011
names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
counts = Hash.new(0)
names.each { |name| counts[name] += 1 }
# => {"Jason" => 2, "Teresa" => 1, ....
28 голосов
/ 02 января 2018

Ruby v2.4 + (текущий)

Следующий код был невозможен в стандартном ruby, когда этот вопрос был впервые задан (февраль 2011 г.), так как он использует:

  • Object#itself, который был добавлен в Ruby v2.2.0 (выпущен в декабре 2014 года).
  • Hash#transform_values, который был добавлен в Ruby v2.4.0 (выпущенДекабрь 2016).

Эти современные дополнения к Ruby обеспечивают следующую реализацию:

names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

names.group_by(&:itself).transform_values(&:count)
#=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

Ruby v2.2 + (устарело)

Если используется более старая версияверсия ruby, без доступа к вышеупомянутому методу Hash#transform_values, вместо этого вы можете использовать Array#to_h, который был добавлен в Ruby v2.1.0 (выпущен в декабре 2013 года):

names.group_by(&:itself).map { |k,v| [k, v.length] }.to_h
#=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

Для даже более старых версий ruby ​​(<= 2.1) есть несколько способов решить эту проблему, но (на мой взгляд) не существует четкого «лучшего» способа.См. Другие ответы на этот пост.


(февраль 2019 г.) Редактировать:

Ruby v2.7 + (еще не выпущен)

Считайте этот комментарий заполнителемна будущее;Я обновлю этот пост, когда будет выпущен ruby ​​2.7.0 (ожидается в декабре 2019 г.), чтобы подтвердить, что метод работает на языке ядра.

В последнее время улучшено в языке .Если все пойдет по плану, мы ожидаем увидеть новый метод, Enumerable#tally, добавленный к ruby ​​v2.7.0.Этот метод добавляет новый синтаксис специально для этой проблемы:

names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

names.tally
#=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
25 голосов
/ 02 января 2016

Теперь, используя Ruby 2.2.0, вы можете использовать метод itself .

names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
counts = {}
names.group_by(&:itself).each { |k,v| counts[k] = v.length }
# counts > {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
16 голосов
/ 26 февраля 2011

На самом деле существует структура данных, которая делает это: MultiSet.

К сожалению, в базовой библиотеке Ruby или стандартной библиотеке нет реализации MultiSet, но есть пара реализаций, плавающих вокругweb.

Это отличный пример того, как выбор структуры данных может упростить алгоритм.Фактически, в этом конкретном примере алгоритм даже полностью исчезает.Это буквально просто:

Multiset.new(*names)

И все.Пример использования https://GitHub.Com/Josh/Multimap/:

require 'multiset'

names = %w[Jason Jason Teresa Judah Michelle Judah Judah Allison]

histogram = Multiset.new(*names)
# => #<Multiset: {"Jason", "Jason", "Teresa", "Judah", "Judah", "Judah", "Michelle", "Allison"}>

histogram.multiplicity('Judah')
# => 3

Пример использования http://maraigue.hhiro.net/multiset/index-en.php:

require 'multiset'

names = %w[Jason Jason Teresa Judah Michelle Judah Judah Allison]

histogram = Multiset[*names]
# => #<Multiset:#2 'Jason', #1 'Teresa', #3 'Judah', #1 'Michelle', #1 'Allison'>
12 голосов
/ 14 апреля 2015

Enumberable#each_with_object избавляет вас от возврата окончательного хэша.

names.each_with_object(Hash.new(0)) { |name, hash| hash[name] += 1 }

Возвращает:

=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
6 голосов
/ 28 февраля 2011

Ниже приведен чуть более функциональный стиль программирования:

array_with_lower_case_a = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
hash_grouped_by_name = array_with_lower_case_a.group_by {|name| name}
hash_grouped_by_name.map{|name, names| [name, names.length]}
=> [["Jason", 2], ["Teresa", 1], ["Judah", 3], ["Michelle", 1], ["Allison", 1]]

Одним из преимуществ group_by является то, что вы можете использовать его для группировки эквивалентных, но не полностью идентичных элементов:

another_array_with_lower_case_a = ["Jason", "jason", "Teresa", "Judah", "Michelle", "Judah Ben-Hur", "JUDAH", "Allison"]
hash_grouped_by_first_name = another_array_with_lower_case_a.group_by {|name| name.split(" ").first.capitalize}
hash_grouped_by_first_name.map{|first_name, names| [first_name, names.length]}
=> [["Jason", 2], ["Teresa", 1], ["Judah", 3], ["Michelle", 1], ["Allison", 1]]
5 голосов
/ 26 февраля 2011

Это работает.

arr = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
result = {}
arr.uniq.each{|element| result[element] = arr.count(element)}
4 голосов
/ 14 августа 2014
a = [1, 2, 3, 2, 5, 6, 7, 5, 5]
a.each_with_object(Hash.new(0)) { |o, h| h[o] += 1 }

# => {1=>1, 2=>2, 3=>1, 5=>3, 6=>1, 7=>1}

Кредит Фрэнк Вамбутт

2 голосов
/ 21 ноября 2017

Здесь много отличных реализаций.

Но я бы посчитал, что это проще всего прочитать и реализовать

names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

name_frequency_hash = {}

names.each do |name|
  count = names.count(name)
  name_frequency_hash[name] = count  
end
#=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

Шаги, которые мы предприняли:

  • мы создали хеш
  • , который мы зациклили на массиве names
  • мы посчитали, сколько раз каждое имя появилось в массиве names
  • мы создали ключ, используя name и значение, используя count

Это может быть немного более многословно (и с точки зрения производительности вы будете выполнять ненужную работу с переопределением ключей),но по моему проще читать и понимать для чего ты хочешь добиться

...