Треки
/
jq
jq
/
Вправи
/
Статистика оцінок
Статистика оцінок

Статистика оцінок

Навчальна вправа

Вступ

Reduce

Припустімо, ми хочемо підсумувати масив чисел. Існує багато способів досягти цієї мети.

  • У багатьох мовах цю задачу виражають як цикл зі змінною-акумулятором.

  • Цю задачу можна записати як рекурсивну функцію. У псевдокоді це могло б мати такий вигляд.

    function Add(X, Sum=0):
      if X is empty then
        return Sum
      else
        return Add(rest(X), Sum + first(X))
      end
    end
    

    Такий метод поділу задачі на менші частини можна також описати як «зведення до базового випадку».

Reduce - це спосіб поєднати всі елементи структури даних в одне значення. Цей процес перебирає структуру даних, застосовуючи функцію до кожного елемента, щоб оновити накопичений результат.

У jq цей процес реалізовано у фільтрі reduce. В інших мовах його можуть називати «fold», «fold-left», «inject» або «aggregate».

Вираз reduce у jq має такий вигляд.

reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
  • STREAM_EXPRESSION - це потік елементів, кожен з яких по черзі зберігається у змінній $var.
  • INITIAL_VALUE - це початкове значення накопиченого результату (відоме як «акумулятор»).
  • UPDATE_EXPRESSION поєднує («згортає») поточне значення ($var) з акумулятором.
    • У контексті цього виразу . - це значення акумулятора.
    • Результат виразу зберігається в акумуляторі для використання в наступній ітерації.
    • Після останньої ітерації накопичений результат стає результатом reduce.

Розглянемо приклад: додавання чисел у масиві. Фільтр add робить саме це, але ми побачимо, як його реалізувати.

Якщо взяти [10, 20, 30, 40] як вхідні дані та нуль як початковий стан, кожен крок матиме такий вигляд.

# стан елемент згортка результат
1 0 10 0 + 10 10
2 10 20 10 + 20 30
3 30 30 30 + 30 60
4 60 40 60 + 40 100

У синтаксисі jq це має такий вигляд.

0 + 10 | . + 20 | . + 30 | . + 40

Це можна виразити за допомогою фільтра reduce.

[10, 20, 30, 40] | reduce .[] as $n (0; . + $n)     # => 100
Note

Вбудований add насправді реалізовано через reduce, але він використовує «null» як початковий стан (до null можна додати будь-який тип даних).

def add: reduce .[] as $x (null; . + $x);

Дещо, що варто памʼятати

  • У виразі згортки . - це акумулятор. Якщо вхідні дані - це якийсь обʼєкт, до якого потрібно звернутися всередині функції згортки, його треба зберегти у змінній.

    {"apple": 10, "banana": 16, "carrot": 4}
    | . as $obj
    | reduce (keys | .[]) as $key (0; . + $obj[$key])     # => 30
    
  • Акумулятор може бути будь-якого типу даних. Наприклад, може знадобитися обернути масив.

    ["A", "B", "C", "D"]
    | reduce .[] as $elem ([]; [$elem] + .)       # => ["D", "C", "B", "A"]
    

Вказівки

Ми працюємо вчителями. Наприкінці року ми згенерували числову оцінку для кожного нашого учня. Тепер нам потрібно перетворити це на літерну оцінку та порахувати, скільки учнів отримали кожну літерну оцінку

1. Перетворити числову оцінку на літерну оцінку

Функція letter_grade прийматиме числову оцінку як вхідні дані та повертатиме літеру. Використовуйте ці діапазони:

Літера Оцінка
A 90% - 100%
B 80% - 89%
C 70% - 79%
D 60% - 69%
F 0% - 59%

Приклад:

75 | letter_grade   # => "C"

2. Порахувати кількість учнів для кожної літерної оцінки

Функція count_letter_grades прийматиме обʼєкт, який зіставляє імена учнів з їхніми оцінками. Вихідні дані будуть обʼєктом, який зіставляє кожну літерну оцінку з кількістю учнів, які мають цю оцінку.

Приклад:

{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}

Існує кілька різних способів розвʼязати це. Використовуйте фільтр reduce для практики.

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
jq Exercism

Час розпочати Статистика оцінок?

Зареєструйтеся на Exercism, щоб вивчати й опановувати jq, а також 12 концепцій75 вправ та справжнє наставництво від людей, і все це безкоштовно.