یک شیء JSON، اگر از اصطلاحات زبانهای دیگر استفاده کنیم، همان «hash»، «map» یا «dictionary» است.
JSON یک شیء را اینگونه تعریف میکند:
یک شیء مجموعهای بدون ترتیب از جفتهای name/value است. هر شیء با
{که همان آکولاد چپ است شروع میشود و با}که همان آکولاد راست است پایان مییابد. هر name با:که همان دونقطه است دنبال میشود و جفتهای name/value با,که همان ویرگول است از هم جدا میشوند.
name باید یک رشته باشد. واژهی دیگری برای name، key است.
value میتواند از هر نوع JSON باشد. _value_های مختلف در یک شیء یکسان میتوانند انواع مختلفی داشته باشند، مانند این مثال.
{
"name": "Jane",
"age": 42,
"pets": ["cat", "fish"],
"address": {"street": "123 Main St", "city": "Springfield"}
}
توجه کنید که پس از آخرین جفت کلید-مقدار نباید ویرگولی بیاید.
برای جمعآوری جفتهای name/value از آکولاد استفاده کنید. هرچند نامها باید رشته باشند، اگر نامها شبیه شناسه باشند (از نویسههای الفبایی-عددی و زیرخط تشکیل شده باشند و با رقم شروع نشوند) نیازی نیست داخل گیومه قرار بگیرند.
{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 استفاده کنید.
تابع معادلی برای فهرست کردن همهی _value_ها وجود ندارد.
با این حال، فیلتر .[] مقادیر شیء را بهصورت یک stream خروجی میدهد و آن stream را میتوان با سازندهی آرایهی [...] ثبت کرد.
[{first: "Jane", last: "Lopez", status: "awesome!"} | .[]]
# => ["Jane", "Lopez", "awesome!"]
تابع map_values(filter) فیلتر را روی هر value در شیء اعمال میکند.
{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
در این تمرین، روشی را پیادهسازی میکنید که با آن میتوانید امتیازهای بالای محبوبترین بازی سالن بازی محلیتان را پیگیری کنید.
شما ۶ تابع برای پیادهسازی دارید که بیشترشان به کار با شیئی مربوط میشوند که امتیازهای بالا را نگه میدارد.
تابعی به اسم 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}
اگر بازیکن از قبل روی تخته نبود، این تابع باید او را به تخته اضافه کند.
سالن بازی دوشنبهها یک تختهی امتیازات جداگانه نگه میدارد. در پایان روز، هر بازیکنی که روی آن تخته باشد ۱۰۰ امتیاز اضافه میگیرد.
تابع 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