物件

物件 在 jq

4 個練習

關於 物件

用其他語言的術語來說,JSON 的物件就是「hash」、「map」或「字典」。

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 個練習,就能練習 物件