Ruby вычисляет процент элементов в хеше

У меня есть хэш:

hash = {"str1"=>2, "str2"=>3, "str3"=>7}

Я хочу рассчитать процент каждого элемента в хэше, чтобы получить его следующим образом:

{"str1"=>16.66% , "str2"=>25.00%, "str3"=>58.33%}

Любая идея об этом? Спасибо


person media    schedule 09.05.2017    source источник
comment
Для этого используйте Hash#transform_values.   -  person Sagar Pandya    schedule 09.05.2017
comment
Добро пожаловать в Stack Overflow. Пожалуйста, прочитайте Как спросить и минимальный воспроизводимый пример и связанные страницы, а также Как много исследовательской работы ожидается от пользователей Stack Overflow?. Ваш вопрос задан неправильно. Вы искали? Если нет, то почему? Если да, то где и почему не помогло? Вы написали код? Если нет, то почему? Если да, то каков минимальный код, необходимый для демонстрации проблемы, о которой вы спрашиваете?   -  person the Tin Man    schedule 09.05.2017
comment
@theTinMan спасибо. Да, я искал и не нашел то, что искал, поэтому и спросил. Я думаю, что мой вопрос достаточно ясен с примером, который я привел. Я получил ответ, который мне был нужен, всего через несколько минут после вопроса. Я не знаю, почему отрицательный голос за, хотя...   -  person media    schedule 10.05.2017
comment
Наведите указатель мыши на стрелки, чтобы увидеть, что они означают. Если вы искали, вы должны сказать, где и объяснить, почему это не помогло. Это помогает нам избежать дублирования ваших усилий, чтобы помочь вам, и помогает другим найти дополнительные источники. Получение ответа через несколько минут означает, что вам следует подождать, чтобы увидеть, появится ли лучший ответ. Чтобы все сообщество увидело вопрос и сформулировало ответ, требуется около 24 часов. Помните, что SO здесь не только для того, чтобы решить вашу проблему, но и для того, чтобы задокументировать решения для всех в будущем, поэтому важны полные вопросы.   -  person the Tin Man    schedule 11.05.2017


Ответы (3)


Вы можете использовать Enumerable#each_with_object:

sum = a.values.inject(0, :+) # or simply a.values.sum if you're on Ruby 2.4+
#=> 12
a.each_with_object({}) { |(k, v), hash| hash[k] = v * 100.0 / sum }
#=> {"str1"=>16.666666666666668, "str2"=>25.0, "str3"=>58.333333333333336}

Чтобы иметь это с %:

a.each_with_object({}) { |(k, v), hash| hash[k] = "#{(v * 100.0 / sum).round(2)}%" }
#=> {"str1"=>"16.67%", "str2"=>"25.0%", "str3"=>"58.33%"}
person Andrey Deineko    schedule 09.05.2017
comment
@AndreyDeineko спасибо большое! Я добавил round(2), чтобы получить только 2 числа после комы. Не могли бы вы объяснить больше { |(k, v), hash| hash[k] = , чтобы я мог лучше понять .. Спасибо - person media; 09.05.2017
comment
@media Я отредактировал ответ со ссылкой на документацию. В основном вы перебираете каждую пару ключ-значение (k,v) и создаете новый объект на лету ({}) - хеш. Таким образом, перед первой итерацией хэш пуст (это начальное значение, которое мы дали). На первой итерации мы устанавливаем hash["str1"] равным расчетному проценту на основе v (2). На следующей итерации мы устанавливаем hash["str2"] для следующего расчета и так далее. В итоге у нас есть новый хэш-объект. - person Andrey Deineko; 09.05.2017
comment
'%.2f%' % (v * 100.0 / sum) может быть более читабельным, если вы привыкли к sprintf. - person Eric Duminil; 09.05.2017
comment
@EricDuminil Я был на 100% уверен, что рано или поздно кто-нибудь упомянет об этом :) Раньше я использовал интерполяцию - '%.2f%' напоминает мне Java, которую я не очень люблю. Но на самом деле это может выглядеть чище для тех, кто к этому привык. - person Andrey Deineko; 09.05.2017
comment
В реальной жизни вам придется иметь дело со случаем, когда sum равно нулю. - person Cary Swoveland; 14.05.2017

Лучший ответ ИМХО был, к сожалению, удален:

total = hash.values.sum
hash.transform_values { |v| (v * 100.0 / total).round(2) }

Метод хеширования transform_values относительно неизвестен, и именно для этого случая он и предназначен. (Ruby 2.4+ или Rails 4.2+)

@Ursus, если ты восстановишь свое, я удалю это. Имейте в виду, что ответы здесь предназначены не только для OP, но и для всех, у кого возникнет такой же вопрос в будущем.

person Mark Thomas    schedule 09.05.2017
comment
Большое спасибо, Марк, но мой ответ был почти идентичен ответу Андрея, и я использовал удивительный метод transform_values, который слишком нов, чтобы использовать его в ответе без альтернативы :) - person Ursus; 09.05.2017
comment
Ну думаю со временем он будет становиться все более и более полезным. Некоторые из моих ответов, которые требовали определенной версии Ruby, возможно, не были слишком полезными в то время, но в конечном итоге стал одним из моих самых результативных ответов. - person Mark Thomas; 09.05.2017

Спасибо @AndreyDeineko за быстрый ответ! Я пытался сделать это только методом each (вместо each_with_object, так что вот ответ.

sum = hash.values.sum
result = {}
hash.each { |k,v| result[k] = (v*100.00/sum).round(2)}

Чтобы иметь это с %:

hash.each { |k,v| result[k] = (v*100.00/sum).round(2).to_s + "%"}
person media    schedule 09.05.2017