Якщо скористатися термінологією з інших мов, JSON-обʼєкт - це «хеш», «мапа» або «словник».
JSON визначає обʼєкт так:
Обʼєкт - це невпорядкована множина пар імʼя/значення. Обʼєкт починається з
{лівої фігурної дужки й закінчується}правою фігурною дужкою. Після кожного імені йде:двокрапка, а пари імʼя/значення розділяються,комою.
Імʼя має бути рядком тексту (англ. string). Інше слово для імені - ключ.
Значення може бути будь-якого типу JSON. Різні значення в одному обʼєкті можуть мати різні типи, як у цьому прикладі.
{
"name": "Jane",
"age": 42,
"pets": ["cat", "fish"],
"address": {"street": "123 Main St", "city": "Springfield"}
}
Зауважмо, що після останньої пари ключ-значення не повинно стояти коми.
Щоб зібрати пари імʼя/значення, використовуймо фігурні дужки. Хоча імена мають бути рядками тексту, їх не обовʼязково брати в лапки, якщо імена схожі на ідентифікатори (складаються з буквено-цифрових символів і символу підкреслення та не починаються з цифри).
{name: "Jane", age: 42}
Цілком припустимо використовувати ключі, що не схожі на ідентифікатори. Просто берімо їх у лапки.
{"first name": "Jane", "last name": "Lopez", age: 42}
Якщо імʼя є результатом виразу, то вираз має бути в дужках.
$ echo "Jane" | jq -Rc '{.: 42}'
# verbose error message ...
$ echo "Jane" | jq -Rc '{(.): 42}'
{"Jane":42}
Значення з обʼєкта дістають за допомогою точкової нотації.
{name: "Jane", age: 42} | .age # => 42
Якщо до ключа не вдається звернутися як до ідентифікатора, скористаймося нотацією з квадратними дужками.
"name" as $key | {name: "Jane", age: 42} | .$key # => error
"name" as $key | {name: "Jane", age: 42} | .[$key] # => "Jane"
Щоб додати нову пару ключ-значення до масиву або оновити значення наявного ключа, використовуймо оператор присвоєння =, де з лівого боку стоїть вираз з індексом.
{name: "Jane", age: 42} | .sport = "tennis" | .age = 21
# => {
# "name": "Jane",
# "age": 21,
# "sport": "tennis"
# }
Оператор + обʼєднує обʼєкти.
{Richard: 54} + {Jane: 42}
# => {
# "Richard": 54,
# "Jane": 42
# }
Щоб видалити ключ, скористаймося функцією del.
Вона повертає оновлений обʼєкт.
{name: "Jane", age: 42} | del(.age) # => {"name": "Jane"}
Параметр функції del - це вираз з індексом (з точковою нотацією або нотацією з квадратними дужками), який вказує на ключ в обʼєкті.
jq називає його виразом шляху.
Просто передати рядок тексту недостатньо.
{name: "Jane", age: 42} | del(name) # error: name/0 is not defined
{name: "Jane", age: 42} | del("name") # error: Invalid path expression with result "name"
{name: "Jane", age: 42} | del(.name) # OK
{name: "Jane", age: 42} | del(.["name"]) # OK
Щоб перевірити, чи містить обʼєкт ключ, скористаймося функцією has.
{name: "Jane", age: 42} as $example
|
($example | has("name")), # => true
($example | has("sport")) # => false
Щоб перевірити, чи є ключ в обʼєкті, скористаймося in.
{name: "Jane", age: 42} as $example
|
("name" | in($example)), # => true
("sport" | in($example)) # => false
Щоб вивести список усіх ключів, скористаймося функцією keys.
{name: "Jane", age: 42} | keys # => ["age", "name"]
Зауважмо, що keys сортує ключі.
Щоб отримати ключі в початковому порядку, скористаймося keys_unsorted.
Немає рівнозначної функції, щоб перелічити всі значення.
Утім, фільтр .[] виводить значення обʼєкта як потік, і цей потік можна зібрати конструктором масиву [...].
[{first: "Jane", last: "Lopez", status: "awesome!"} | .[]]
# => ["Jane", "Lopez", "awesome!"]
Функція map_values(filter) застосовує фільтр до кожного значення в обʼєкті.
{first: "Jane", last: "Lopez", status: "awesome!"}
| map_values(ascii_upcase)
# => {"first": "JANE", "last": "LOPEZ", "status": "AWESOME!"}
Щоб перебрати обʼєкт, спершу треба перетворити його на масив обʼєктів ключ-значення.
Саме це робить функція to_entries.
{name: "Jane", age: 42} | to_entries'
# => [
# {
# "key": "name",
# "value": "Jane"
# },
# {
# "key": "age",
# "value": 42
# }
# ]
Тепер ми можемо скористатися функціями ітерації масивів, як-от map.
Функція from_entries виконує зворотну дію: перетворює масив обʼєктів ключ-значення на обʼєкт.
[
{"key":"name", "value":"Jane"},
{"key":"age", "value":42}
] | from_entries # =>{"name": "Jane", "age": 42}
Щоб застосувати фільтр до кожної пари ключ-значення в обʼєкті, скористаймося функцією with_entries(filter).
Наприклад, маючи обʼєкт, який зіставляє імʼя з віком, можна поміняти місцями ключі та значення так.
{"Jane": 42, "Richard": 54}
| with_entries({key: (.value | tostring), value: .key})
виводить
{
"42": "Jane",
"54": "Richard"
}
with_entries(filter) - це те саме, що
to_entries | map(filter) | from_entries
У цій вправі ми реалізуємо спосіб відстежувати рекорди для найпопулярнішої гри у місцевому залі ігрових автоматів.
Нам потрібно реалізувати 6 функцій, переважно повʼязаних із роботою з обʼєктом, який зберігає рекорди.
Напишіть функцію create_score_board, яка створює обʼєкт, що править за таблицю рекордів.
Ключами цього обʼєкта будуть імена гравців, а значеннями - їхні рахунки.
Для тестування потрібно безпосередньо включити до обʼєкта один запис.
Цей початковий запис має складатися з "The Best Ever" як імені гравця та 1000000 як рахунку.
create_score_board
# returns an object with one initial entry
Щоб додати гравця до таблиці рекордів, реалізуйте функцію add_player.
Вона приймає таблицю рекордів як вхідні дані та потребує два параметри: імʼя гравця та рахунок гравця.
Функція повертає обʼєкт таблиці рекордів із доданим новим гравцем.
{"José Valim", 486373}
| add_player("Dave Thomas"; 0)
# => {"Dave Thomas": 0, "José Valim": 486373} -- in some order
Якщо гравець уже є в таблиці, поверніть таблицю без змін.
Якщо гравці порушують правила залу ігрових автоматів, їх вручну видаляють із таблиці рекордів.
Реалізуйте remove_player, яка приймає таблицю як вхідні дані та один параметр: імʼя гравця, якого потрібно видалити.
Ця функція має видалити запис указаного гравця з таблиці та повернути нову таблицю.
Якщо гравця спочатку не було в таблиці, з таблицею нічого не має статися; її слід повернути без змін.
{"Dave Thomas": 0} | remove_player("Dave Thomas")
# => {}
{"Dave Thomas": 0} | remove_player("Rose Fanaras")
# => {"Dave Thomas": 0}
Якщо гравець завершує ще одну гру в залі ігрових автоматів, до попереднього рахунку в таблиці додається певна кількість очок.
Реалізуйте update_score, яка приймає таблицю рекордів як вхідні дані та потребує два параметри: імʼя гравця й кількість очок для додавання.
Функція має повернути таблицю рекордів після виконання оновлення.
{"Freyja Ćirić": 12771000} | update_score("Freyja Ćirić"; 73)
# => {"Freyja Ćirić": 12771073}
Ця функція має додати гравця до таблиці, якщо його там ще не було.
У понеділок зал ігрових автоматів веде окрему таблицю рекордів. Наприкінці дня кожен гравець у цій таблиці отримує 100 додаткових очок.
Реалізуйте функцію apply_monday_bonus.
Функція додає бонусні очки кожному гравцеві, зазначеному в цій таблиці.
{
"Dave Thomas": 44,
"Freyja Ćirić": 539,
"José Valim": 265
}
| apply_monday_bonus
# => {"Dave Thomas": 144, "Freyja Ćirić": 639, "José Valim": 365}
Різні зали ігрових автоматів змагаються між собою, щоб визначити, у кого найкращі гравці. Зал із найвищим загальним рахунком отримує цю честь.
Напишіть функцію total_score.
Вона приймає таблицю рекордів як вхідні дані та повертає суму рахунків усіх гравців.
{
"Dave Thomas": 44,
"Freyja Ćirić": 539,
"José Valim": 265
}
| total_score
# => 848
Зареєструйтеся на Exercism, щоб вивчати й опановувати jq, а також 12 концепцій75 вправ та справжнє наставництво від людей, і все це безкоштовно.