المسارات
/
jq
jq
/
التمارين
/
لوحة أفضل النتائج
لوحة أفضل النتائج

لوحة أفضل النتائج

تمرين تعلّمي

مقدمة

الكائنات

الكائن في JSON هو، بمصطلحات اللغات الأخرى، "hash" أو "map" أو "dictionary".

يعرّف 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 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.