Как найти количество уникальных вхождений для массива в Ruby - PullRequest
0 голосов
/ 04 июня 2019

У меня есть массив, содержащий n количество элементов.Каждый элемент содержит два слова.

Это делает массив похожим на это: ['England John', 'England Ben', 'USA Paul', 'England John']

Я хочу найти количество уникальных имен для каждой страны.Например, England будет иметь 2 уникальных имени, поскольку John существует два раза.

Пока что я разделил массив на два массива, один из которых содержит такие страны, как ['England', 'Usa', ...], а другой - имена ['John', 'Paul', ...], однако я не уверен, куда идти дальше

Ответы [ 4 ]

4 голосов
/ 04 июня 2019

Вариант с одним вкладышем:

ary.uniq.group_by { |e| e.split.first }.transform_values(&:count)
#=> {"England"=>2, "USA"=>1}
3 голосов
/ 04 июня 2019

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

Предположим, например, что сначала мы конвертируем эти данные в Hash, который сопоставляет каждую страну со списком имен:

data = ['England John', 'England Ben', 'USA Paul', 'England John']

mapped_names = {}

data.each do |item|
  country, name = item.split
  mapped_names[country] ||= []
  mapped_names[country] << name
end

Теперь получить счет довольно просто:

mapped_name_counts = unique_names.transform_values { |names| names.uniq.count }

Полученные переменные:

mapped_names # => {"England"=>["John", "Ben", "John"], "USA"=>["Paul"]}
mapped_name_counts # => {"England"=>2, "USA"=>1}

И если используется версия 2.7 ruby ​​(еще не выпущена!!) эта последняя строка кода может быть даже упрощена до:

mapped_name_counts = unique_names.tally(&:uniq)
0 голосов
/ 04 июня 2019
arr = ['England John', 'England Ben', 'USA Paul', 'England John']

arr.uniq.each_with_object(Hash.new(0)) { |s,h| h[s[/\S+/]] += 1 }
  #=> {"England"=>2, "USA"=>1}

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

require 'set'

uniques = Set.new
arr.each_with_object(Hash.new(0)) { |s,h| h[s[/\S+/]] += 1 if uniques.add?(s) }
  #=> {"England"=>2, "USA"=>1}

См. Форму Hash :: new , которая принимает аргумент (называемый значением по умолчанию ), а также Set # add? .

Мне не ясно, какой из двух расчетов в общем случае будет быстрее.

0 голосов
/ 04 июня 2019

Немного более многословно, чем другие решения, но не использует transform_values от ActiveSupport.

require "set"

data = ["England John", "England Ben", "USA Paul", "England John", "Switzerland Pascal"]

names_per_country = data.each_with_object({}) do |country_and_name, accu|
  country, name = country_and_name.split(" ")
  country_data = accu[country] ||= Set.new
  country_data << name
end

names_per_country.each do |country, names|
  puts "#{country} has #{names.size} unique name(s)"
end

# => England has 2 unique names
# => USA has 1 unique names
# => Switzerland has 1 unique names

Это решение сначала преобразует массив в структуру Hash, где ключ - это название страны, а значение - Set. Я выбрал Set, потому что он автоматически обрабатывает уникальную часть вашего вопроса (Set не может содержать дубликаты).

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

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