用其他語言的術語來說,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 個函式,大多與操作一個存放最高分的物件有關。
撰寫一個函式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