객체

객체 에서 jq

4개의 연습 문제

객체 소개

JSON 객체는 다른 언어의 용어를 빌리자면 "해시", "맵", "딕셔너리"예요.

JSON은 _객체_를 이렇게 정의해요:

객체는 이름/값 쌍의 순서 없는 집합이에요. 객체는 { 왼쪽 중괄호로 시작하고 } 오른쪽 중괄호로 끝나요. 각 이름 뒤에는 : 콜론이 오고, 이름/값 쌍은 , 쉼표로 구분해요.

_이름_은 반드시 문자열이어야 해요. _이름_을 다른 말로 하면 _키_예요.

_값_은 어떤 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개를 더 잠금 해제해요