트랙
/
jq
jq
/
연습 문제
/
최고 점수 게시판
최고 점수 게시판

최고 점수 게시판

학습 연습 문제

소개

객체

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}'
# 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개 있고, 대부분 최고 점수를 담고 있는 객체를 다루는 것과 관련되어 있어요.

1. 새로운 최고 점수판 만들기

최고 점수판 역할을 하는 객체를 만드는 create_score_board 함수를 작성해요. 이 객체의 키는 플레이어의 이름이고, 값은 플레이어의 점수예요. 테스트를 위해 객체에 항목 하나를 바로 넣어야 해요. 이 첫 항목은 플레이어 이름 "The Best Ever"와 점수 1000000으로 구성되어야 해요.

create_score_board
# returns an object with one initial entry

2. 점수판에 플레이어 추가하기

최고 점수판에 플레이어를 추가하려면 add_player 함수를 구현해요. 이 함수는 점수판을 입력으로 받고, 플레이어 이름과 플레이어의 점수라는 두 개의 매개변수가 필요해요. 이 함수는 새 플레이어가 추가된 점수판 객체를 출력해요.

{"José Valim", 486373}
| add_player("Dave Thomas"; 0)
# => {"Dave Thomas": 0, "José Valim": 486373} -- in some order

플레이어가 이미 점수판에 있다면, 점수판을 그대로 출력해요.

3. 점수판에서 플레이어 제거하기

플레이어가 오락실 규칙을 위반하면, 최고 점수판에서 직접 제거해요. 점수판을 입력으로 받고 제거할 플레이어의 이름을 매개변수 하나로 받는 remove_player를 구현해요. 이 함수는 주어진 플레이어의 항목을 점수판에서 제거하고 새 점수판을 출력해야 해요. 플레이어가 애초에 점수판에 없었다면 점수판에 아무 일도 일어나지 않아야 하고, 그대로 반환해야 해요.

{"Dave Thomas": 0} | remove_player("Dave Thomas")
# => {}

{"Dave Thomas": 0} | remove_player("Rose Fanaras")
# => {"Dave Thomas": 0}

4. 플레이어 점수 올리기

플레이어가 오락실에서 게임을 하나 더 끝내면, 점수판에 있던 이전 점수에 일정량의 점수가 더해져요. 점수판을 입력으로 받고, 플레이어 이름과 더할 점수라는 두 개의 매개변수가 필요한 update_score를 구현해요. 이 함수는 업데이트가 끝난 뒤의 점수판을 반환해야 해요.

{"Freyja Ćirić": 12771000} | update_score("Freyja Ćirić"; 73)
# => {"Freyja Ćirić": 12771073}

이 함수는 플레이어가 아직 점수판에 없다면 점수판에 추가해야 해요.

5. 월요일 보너스 점수 적용하기

오락실은 월요일에 별도의 점수판을 운영해요. 하루가 끝나면 그 점수판에 있는 각 플레이어는 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}

6. 총점 구하기

여러 오락실이 서로 경쟁해서 누가 최고의 플레이어를 가졌는지 겨뤄요. 총점이 가장 높은 오락실이 영예를 차지해요.

total_score 함수를 작성해요. 이 함수는 점수판을 입력으로 받고, 모든 플레이어 점수의 합을 출력해요.

{
  "Dave Thomas": 44,
  "Freyja Ćirić": 539,
  "José Valim": 265
}
| total_score
# => 848
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
jq Exercism

최고 점수 게시판 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 jq 트랙을 개념 12개연습 문제 75개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.