Треки
/
jq
jq
/
Салабус
/
Обʼєкти
Об

Обʼєкти у jq

4 вправи

Про концепцію Обʼєкти

Обʼєкт 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}'
jq: error: syntax error, unexpected '.' (Unix shell quoting issues?) at <top-level>, line 1:
{.: 42}
jq: error: May need parentheses around object key expression at <top-level>, line 1:
{.: 42}
jq: 2 compile errors

$ echo "Jane" | jq -Rc '{(.): 42}'
{"Jane":42}

Скорочені записи

Для кількох поширених випадків створення обʼєктів існує скорочений синтаксис.

  1. Повторне використання пар ключ-значення з вхідного обʼєкта.

    $ jq -n '$ENV | {USER: .USER, EDITOR: .EDITOR}'
    {
      "USER": "glennj",
      "EDITOR": "vim"
    }
    

    $ENV - це обʼєкт, що містить пари імʼя-значення середовища.

    Повторювані пари key: .key можна записати просто як key

    $ jq -n '$ENV | {USER, EDITOR}'
    {
      "USER": "glennj",
      "EDITOR": "vim"
    }
    
  2. Так само існує скорочений запис і для використання змінних.

    $ jq -cn '"foo" as $a | "bar" as $b | {a: $a, b: $b}'
    {"a":"foo","b":"bar"}
    
    $ jq -cn '"foo" as $a | "bar" as $b | {$a, $b}'
    {"a":"foo","b":"bar"}
    

Індексація

Значення з обʼєкта дістають за допомогою точкової нотації.

{name: "Jane", age: 42} | .age    # => 42

Якщо до ключа не можна звернутися як до ідентифікатора, використовують нотацію з квадратними дужками.

"name" as $key | {name: "Jane", age: 42} | .$key    # => error
"name" as $key | {name: "Jane", age: 42} | .[$key]  # => "Jane"

Якщо ключа немає в обʼєкті, результатом буде null.

{name: "Jane", age: 42} | .sport  # => null

Індексація вкладених значень

Ми побачимо, що цю дію доводиться виконувати знову і знову, коли ми розбираємо результати запитів до API.

Маючи обʼєкт, який містить масиви або обʼєкти як значення, ми можемо «зчепити» вирази індексації в ланцюжок.

{
  "name": "Jane",
  "age": 42,
  "pets": ["cat", "fish"],
  "address": {"street": "123 Main St", "city": "Springfield"}
}
| (
  .pets[1],       # => "fish"
  .address.city   # => "Springfield"
)

Додавання та зміна пар ключ-значення

Щоб додати нову пару ключ-значення до масиву або оновити значення наявного ключа, використовують оператор присвоєння =, а з лівого боку записують вираз індексації.

{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
    
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію Обʼєкти

Практика заблокована

Розблокуйте ще 4 вправи, щоб практикувати концепцію Обʼєкти