group_by и partition
Часть раздела Логика и управление потоком путешествия по Ruby на Coddy — урок 25 из 56.
Иногда вам нужно разделить коллекцию, а не фильтровать её. Два метода охватывают большинство случаев.
group_by принимает блок и возвращает хеш. Каждый уникальный результат блока становится ключом, который сопоставляется с массивом элементов, создавших его:
words = ["hi", "hello", "yo", "hey", "bonjour"]
by_length = words.group_by { |w| w.length }
puts by_length.inspect
# {2=>["hi", "yo"], 5=>["hello"], 3=>["hey"], 7=>["bonjour"]}Это самый чистый способ сгруппировать элементы за один проход.
partition — это бинарная версия: она возвращает пару массивов: элементы, для которых блок вернул true, и все остальные:
evens, odds = [1, 2, 3, 4, 5].partition { |n| n.even? }
puts evens.inspect # [2, 4]
puts odds.inspect # [1, 3, 5]Используйте partition, когда есть ровно две корзины, и group_by, когда их может быть много.
Задание
СреднеПрочитайте список записей name:score, разделенных запятыми (например, Alice:88,Bob:42,Charlie:75). Используйте group_by, а затем partition, чтобы создать небольшой отчет.
Шаги:
- Преобразуйте каждую запись в пару
[name, score](score — это целое число) - Выполните
group_byпо первой букве имениnameиз пары (в верхнем регистре) - Для каждой группы букв в алфавитном порядке выполните
partitionзаписей на сдавшие (score >= 60) и не сдавшие, затем выведите по одной строке на группу:
<LETTER>: pass=<names_passing_joined_by_comma> fail=<names_failing_joined_by_comma>Внутри каждой группы имена должны идти в исходном порядке. Если в какой-либо категории нет имен, оставьте её пустой (например, fail=).
Для входных данных Alice:88,Adam:42,Bob:75,Beth:55,Cara:90 вывод будет следующим:
A: pass=Alice fail=Adam
B: pass=Bob fail=Beth
C: pass=Cara fail=Шпаргалка
group_by разделяет коллекцию на хеш, где ключами являются результаты выполнения блока:
words = ["hi", "hello", "yo"]
by_length = words.group_by { |w| w.length }
# {2=>["hi", "yo"], 5=>["hello"]}partition разделяет на ровно два массива (соответствующие и не соответствующие условию):
evens, odds = [1, 2, 3, 4, 5].partition { |n| n.even? }
# evens => [2, 4], odds => [1, 3, 5]Используйте partition для двух групп, group_by для множества.
Попробуйте сами
entries = gets.chomp.split(",").map do |s|
name, score = s.split(":")
[name, score.to_i]
end
# TODO: сгруппировать по первой букве (заглавной), разделить каждую группу по баллу >= 60,
# вывести 'X: pass=... fail=...' в алфавитном порядке букв
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Логика и управление потоком
1Строки в деталях
Обзор методов строкИнтерполяция строкИтерация по строкамSplit и JoinИтоги — String Weaver4Блоки, Proc и лямбды
Что такое блок?do..end против фигурных скобокКлючевое слово yieldПараметры блокаProc и лямбдыИтоги: пользовательский итератор7Хеши. Часть 2
Hash.new со значениями по умолчаниюИтерация по хешамВложенные хешиСлияние и преобразованиеИтоги — счетчик частоты10Проект — Учёт студентов
Обзор проектаДобавить студента2Диапазоны
Что такое диапазон?Методы диапазоновДиапазоны в Case / WhenПовторение — практика по диапазонам5Мощь Enumerable
Select и RejectЦепочки MapReduce / Injectcount, all?, any?, none?group_by и partitionsort_by, min_by, max_byИтоги — Data Pipeline8Продвинутые условные конструкции
Case с классами и RegexМножественные значения в whenТернарный операторОднострочные if / unlessПовторение: Классификатор оценок