مسیرها
/
jq
jq
/
تمرین‌ها
/
فهرست خرید
فهرست خرید

فهرست خرید

تمرین یادگیری

مقدمه

مبانی

فرض می‌کنیم که با نحوه‌ی نگارش و انواع داده‌ی JSON آشنایید.

jq کار خود را با عبور دادن داده‌ی JSON ورودی از یک عبارت واحد (که به‌صورت خط لوله‌ای از فیلترها نوشته می‌شود) انجام می‌دهد تا به داده‌ی تبدیل‌شده‌ی دلخواه برسد.

Note

زبان jq به‌وسیله‌ی برنامه‌ی jq پیاده‌سازی شده است. این برنامه چندین گزینه‌ی کاربردی خط فرمان در اختیار می‌گذارد که کنترل می‌کنند ورودی چگونه مصرف می‌شود و خروجی چگونه نمایش داده می‌شود.

در مثال‌های زیر با این‌ها روبه‌رو می‌شوید:

  • -n یا --null-input

    به‌طور معمول، به برنامه‌ی jq یک فایل برای خواندن داده می‌شود، یا شما داده را به ورودی آن می‌فرستید. گزینه‌ی --null-input به شما اجازه می‌دهد بدون هیچ ورودی‌ای داده‌ی JSON تولید کنید.

  • -c یا --compact-output

    jq به‌طور پیش‌فرض خروجی خود را مرتب و خوانا چاپ می‌کند. وقتی داده مرتب قالب‌بندی شده باشد، مشاهده‌ی آن برای انسان بسیار مفید است. اما این کار برای ماشین‌ها ضروری نیست: گزینه‌ی --compact-output فاصله‌های قالب‌بندی را حذف می‌کند تا حجم JSON حاصل کمینه شود.

  • -f filename یا --from-file filename

    برنامه‌ی jq را از filename بخوانید، به‌جای اینکه آن را روی خط فرمان بنویسید. sed و awk هر دو برای همین منظور از گزینه‌ی -f استفاده می‌کنند. این گزینه را در اسکریپت‌های تست تمرین‌های عملی خواهید دید.

باقی این درس بر خودِ زبان jq تمرکز خواهد کرد.

فیلترها و لوله‌ها

به فیلترها عبارت‌ها هم می‌گویند.

یک فیلتر ورودی می‌گیرد و خروجی تولید می‌کند. مانند روشی که در شل یونیکس کار می‌کنید، می‌توانید فیلترها را با یک لوله | به هم بپیوندید تا خروجی یک فیلتر به ورودی فیلتر دیگری وصل شود.

فیلتر همانی: .

این ساده‌ترین فیلتر است. ورودی خود را بدون هیچ تغییری به خروجی می‌فرستد.

$ echo '[1, 2, 3]' | jq '.'
[
  1,
  2,
  3
]

آرایه

این آشنایی سریعی با کار کردن با آرایه‌ها است. این موضوع را بعداً با جزئیات بیشتری بررسی می‌کنیم.

به عنصرهای آرایه با کروشه دسترسی پیدا می‌کنید و شماره‌گذاری آن‌ها از صفر شروع می‌شود.

$ echo '[10, 20, 30]' | jq '.[1]'
20

یک فیلتر می‌تواند با قرار دادن یک عبارت داخل [ و ] یک آرایه بسازد

  • با فهرستی معلوم از عنصرها:

    jq -n '[1, 2, 3]'
    
  • برای گردآوری یک جریان از عنصرها: برای نمونه، range تابعی است که جریانی از عددها تولید می‌کند

    $ jq -n 'range(10; 70; 15)'
    10
    25
    40
    55
    

    استفاده از [] نتیجه‌های عبارت را در یک آرایه گرد می‌آورد

    $ jq -c -n '[range(10; 70; 15)]'
    [10,25,40,55]
    

کاما یک عملگر است

کاما فقط نحوه‌ی نگارشی نیست که عنصرهای آرایه را از هم جدا کند. کاما یک عملگر است که جریان‌ها را به هم می‌پیوندد.

برای نمونه، [1, 2, 3] یک فیلتر است که با استفاده از سازنده‌ی آرایه یعنی []، نتیجه‌ی پیوند سه عبارت 1، 2 و 3 را گرد می‌آورد.

آیا نقطه‌ویرگول‌های range(10; 70; 15) را در بالا متوجه شدید؟ چون کاماها در زبان jq کاربرد مشخصی دارند، توابعی که چند آرگومان می‌گیرند آرگومان‌ها را با نقطه‌ویرگول از هم جدا می‌کنند.

شیء

آشنایی سریعی با شیءها.

مانند بسیاری از زبان‌های برنامه‌نویسی، برای دسترسی به ویژگی‌های شیء از نقطه استفاده کنید

$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"
Note

از کروشه‌ها می‌توان برای شیءها هم استفاده کرد، اما در آن صورت برای رشته‌های ثابت به گیومه نیاز دارید. این یکی از روش‌های کار با کلیدهایی است که فاصله دارند.

$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"

می‌توانید یک شیء را با {} و جفت‌های key: value بسازید. دور کلیدهایی که رشته‌های «ساده» هستند نیازی به گیومه نیست.

jq -n '{question: (6 * 9), answer: 42}'

خروجی می‌دهد

{
  "question": 54,
  "answer": 42
}

برای اینکه با کلید مانند یک عبارت رفتار شود، باید آن را داخل پرانتز بگذارید (مثال زیر هم همان خروجی بالا را می‌دهد).

echo '[{"key":"question", "value":54}, {"key":"answer", "value":42}]' \
| jq '{(.[0].key): .[0].value, (.[1].key): .[1].value}'

خطوط لوله

برای نمونه، فرض کنید file.json این را در خود دارد

{
  "key1": "value1",
  "key2": [5, 15, 25]
}

بیایید طول آرایه‌ی key2 را حساب کنیم:

$ jq '.key2 | length' file.json
3

ما خروجی عبارت .key2 را به‌عنوان ورودی به length لوله می‌کنیم و این تابع، چنان‌که انتظار می‌رود، تعداد عنصرهای آرایه را تولید می‌کند.

فیلترها می‌توانند ورودی خود را نادیده بگیرند

در این مثال، داده‌ی JSON ورودی نادیده گرفته می‌شود و هیچ تأثیری بر خروجی ندارد:

$ echo '{"answer": 42}' | jq '6 * 9'
54

فیلترها می‌توانند جریان‌هایی از داده تولید کنند

یک فیلتر می‌تواند بیش از یک مقدار تولید کند. برای نمونه، فیلتر .[] هر عنصر از یک آرایه را به‌صورت یک مقدار جداگانه تولید می‌کند:

$ jq -n -c '[1, 2, 3]'
[1,2,3]

$ jq -n -c '[1, 2, 3] | .[]'
1
2
3

لوله کردن چنین فیلتری به فیلتر دیگر، فیلتر دوم را برای هر مقدار اجرا می‌کند:

$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6

این شبیه تکرار ضمنی است. وقتی این تکنیک را درک کنید، متوجه می‌شوید که فیلترهای بسیار قدرتمند jq می‌توانند بسیار موجز باشند.

پرانتزها

از پرانتزها برای گروه‌بندی زیرعبارت‌ها و اعمال ترتیب انجام عملیات استفاده می‌شود، درست مانند زبان‌های دیگر. در jq، نیاز به آن‌ها ممکن است تا حدی غافلگیرکننده به نظر برسد.

برای نمونه، فرض کنید می‌خواهیم یک آرایه با ۲ عنصر بسازیم: جذر ۹؛ و e به توان ۱. دو عبارت جداگانه 9 | sqrt و 1 | exp هستند. انتظار داریم خروجی آرایه‌ی [3, 2.7...] باشد.

$ jq -n '[ 9|sqrt, 1|exp ]'
[
  20.085536923187668,
  2.718281828459045
]

چرا آنچه انتظار داشتیم به دست نیامد؟ jq آن را این‌طور تفسیر می‌کند:

[ ((9|sqrt), 1) | exp ]

jq جریانی از دو عنصر (3 و 1) می‌سازد که هر یک به exp داده می‌شوند.

باید مطمئن شویم که exp فقط یک عدد را به‌عنوان ورودی می‌گیرد. به عبارت دیگر، باید ترتیبی بدهیم که لوله پیش از کاما ارزیابی شود.

$ jq -n '[ 9|sqrt, (1|exp) ]'
[
  3,
  2.718281828459045
]

توابع و عملگرها

بدون اینکه خیلی عمیق شویم (توابع موضوع تمرین دیگری خواهند بود)، در ادامه چند تابع توکار مفید را می‌بینید:

  • length

    با گرفتن یک آرایه به‌عنوان ورودی، تعداد عنصرهای آرایه را تولید می‌کند.

    $ jq -n '[10, 20, 30, 40] | length'
    4
    
  • +

    این عملگر بسته به نوع عملوندهایش کارهای متفاوتی انجام می‌دهد: عددها را جمع می‌کند، رشته‌ها را به هم می‌چسباند، آرایه‌ها را به هم می‌افزاید، و شیءها را ادغام می‌کند.

    $ jq -c -n '
        3 + 4,
        "foo" + "bar",
        ["a", "b"] + ["c"],
        {"m": 10} + {"n": 20}
    '
    7
    "foobar"
    ["a","b","c"]
    {"m":10,"n":20}
    

    add تابعی است که یک آرایه می‌گیرد و عنصری برمی‌گرداند که همه‌ی عنصرهایش با قواعد + با هم جمع شده‌اند. [1, 2, 3] | add خروجی 6 را می‌دهد.

  • map

    با گرفتن یک آرایه به‌عنوان ورودی و یک فیلتر به‌عنوان آرگومان، آرایه‌ای تولید می‌کند که فیلتر روی هر عنصر آن اعمال شده است

    $ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
    [2,4,6,8]
    
  • select

    با گرفتن مقداری ورودی و یک فیلتر به‌عنوان آرگومان:

    • اگر اعمال فیلتر روی آرگومان به یک مقدار «درست» منجر شود، ورودی را بدون تغییر تولید می‌کند
    • در غیر این صورت، هیچ‌چیز تولید نمی‌کند (نه مقدار null، بلکه واقعاً هیچ خروجی‌ای)

    برای نمونه، با گرفتن چند عدد، آن‌هایی که بر ۳ بخش‌پذیرند را انتخاب کنید

    $ jq -n 'range(10) | select(. % 3 == 0)'
    0
    3
    6
    9
    

    به یاد بیاورید که range یک جریان از عددها تولید می‌کند. select یک بار برای هر عدد فراخوانی می‌شود. فقط عددهایی تولید می‌شوند که از عبارت «عبور» کنند.

    اغلب لازم است عنصرهایی از یک آرایه را انتخاب کنید. چند راه برای این کار وجود دارد.

    با ورودی ["Anne", "Bob", "Cathy", "Dave"]، اسم‌هایی را انتخاب کنید که طولشان ۴ است.

    • map و select را با هم به کار ببرید

      map(select(length == 4))
      
    • آرایه را به عنصرها باز کنید، روی آن جریان select اعمال کنید و نتیجه‌ها را گرد آورید

      [ .[] | select(length == 4) ]
      

کامنت‌ها

کامنت‌ها با کاراکتر # شروع می‌شوند و تا پایان خط ادامه می‌یابند.

دستورالعمل‌ها

یک‌دفعه هوس پنکیک کرده‌اید. به عنوان یک آدم مدرن و اهل فناوری، فهرست خرید مواد اولیه‌ی پنکیک را در قالب JSON می‌نویسید. ساختار این فهرست چنین است:

{
  "name": "name of shopping list",
  "ingredients": [ ...list of ingredients... ],
  "optional ingredients": [ ...list of ingredients... ]
}

هر ماده‌ی اولیه به شکل یک شیء JSON نمایش داده می‌شود، مانند:

{
  "item": "flour",
  "amount": {
    "quantity": 2,
    "unit": "cup"
  }
}

هر ماده‌ی اولیه یک ویژگی اختیاری به اسم "substitute" دارد که رشته‌ای از نام ماده‌ای دیگر را نگه می‌دارد تا بتوان به‌جای آن از آن استفاده کرد.

بیایید ببینیم چطور می‌توانیم فهرست خرید را با jq بررسی کنیم

1. استخراج اسم فهرست خرید

عبارتی بنویسید که عنصر "name" فهرست خرید را خروجی بدهد.

2. شمارش تعداد مواد اولیه‌ی لازم

فقط مواد اولیه‌ی "required" را بشمارید. مواد اولیه‌ی اختیاری را حساب نکنید.

3. چقدر شکر؟

عبارتی بنویسید که مقدار شکر را خروجی بدهد. فقط بخش عددی مورد نظر است.

4. نگاشت مواد اولیه‌ای که می‌توان جایگزین کرد

برخی از مواد اولیه را می‌توان جایگزین کرد (اگر ماده‌ی اولیه‌ی X را ندارید، می‌توانید از Y استفاده کنید). یک شیء JSON خروجی بدهید که ماده‌ی اولیه‌ی پیشنهادی را به جایگزینش نگاشت می‌کند. مواد اولیه‌ی اختیاری را هم در این نگاشت بگنجانید.

ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
jq Exercism

آماده‌اید فهرست خرید را شروع کنید؟

در Exercism ثبت‌نام کنید تا jq را همراه با 12 مفهوم75 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.