العمليات علي المصفوفات

الدالة map

يتم تطبيق الفلتر علي جميع عناصر المصفوفة

وهو مكافئ لتطبيق [.[] | filter]

$ echo [1, 2, 3] | jq 'map(.+1)' 
[2, 3, 4]

// equivalent to:
$ echo [1, 2, 3] | jq '[.[] | . +1]'
[2, 3, 4]

$ echo '{"a": 1, "b": 2, "c": 3}' | jq 'map(.+1)'
[2, 3, 4]

عند تطبيق map علي object فانه تم تطبيق الفلتر علي قيم الـ object ورجع بمصفوفة, ولارجاع object بدلا من ذلك يمكن استخدام map_values

$ echo '{"a": 1, "b": 2, "c": 3}' | jq 'map_values(.+1)'
{"a": 2, "b": 3, "c": 4}

تطبيق map أو map_values علي مصفوفة سيعطي دائما نفس النتيجة

تطبيق map علي مصفوفة او object دائما يعطي مصفوفة

تطبيق map_values علي object يعطي object

يمكن لـ map أن تأخذ عدة فلاتر وستقوم بتطبيق جميع الفلاتر علي كل عنصر

أما map_value ستقوم بتطبيق أول فلتر فقط علي جميع العناصر

$ echo [1, 2, 3] | jq 'map(. , . + 10)'
[1, 11, 2, 12, 3, 13]

$ echo [1, 2, 3] | jq 'map_values(. , . + 10)'
[1, 2, 3]

$ echo '{"a": 1, "b": 2, "c": 3}' | jq 'map(. , . + 10)'
[1, 11, 2, 12, 3, 13]

$ echo '{"a": 1, "b": 2, "c": 3}' | jq 'map_values(. , . + 10)'
{"a": 1, "b": 2, "c": 3}

ويمكن اضافة جميع عناصر المصفوفة الي بعضهم البعض باستخدام add

$ echo '[1, 2, 3]' | jq add
6

$ echo '["a", "b", "c"]' | jq add
"abc"

$ echo '[]' | jq add
null

$ echo '[{"a":3}, {"a":5}, {"b":6}]' | jq 'add'
{"a": 5, "b": 6}

$ echo '[{"a":3}, {"a":5}, {"b":6}]' | jq 'map(.a) | add'
8

# or
$ echo '[{"a":3}, {"a":5}, {"b":6}]' | jq 'add(.[].a)'
8

حذف عنصر من مصفوفة أو object باستخدام del

$ echo '{"foo": 42, "bar": 9001, "baz": 42}' | jq 'del(.foo)'
{"bar": 9001, "baz": 42}

$ echo '["foo", "bar", "baz"]' | jq 'del(.[1, 2])'
["foo"]

// equivalent to (using Subtraction `-`):
$ echo '["foo", "bar", "baz"]' | jq '. - [.[1], .[2]]'
["foo"]

اختيار العناصر التي تحقق شروط معينة باستخدام select

الدالة select تأخذ شرط محدد وتقوم بارجاع قيمة boolean, عندما تقوم بارجاع true يتم اختيار العنصر, وعندما تقوم بارجاع false يتم حذف العنصر

$ echo '[1,5,3,0,7]' | jq '.[] | select(. > 2)'
5
3
7

$ echo '[1,5,3,0,7]' | jq '[.[] | select(. > 2)]'
[5, 3, 7]

$ echo '[1,5,3,0,7]' | jq 'map(select(. >= 2))'
[5, 3, 7]

$ echo '[{"id": "first", "val": 1}, {"id": "second", "val": 2}]' | jq '.[] | select(.id == "second")'
{"id": "second", "val": 2}

ويمكن اختيار العناصر حسب نوعها باستخدام arrays, objects, iterables, booleans, numbers, normals, finites, strings, nulls, values, scalars

$ echo '[[],{},1,"foo",null,true,false]' | jq '.[] | numbers'
1

$ echo '[[],{},1,"foo",null,true,false]' | jq '.[] | strings'
"foo"

$ echo '[[],{},1,"foo",null,true,false]' | jq '.[] | arrays'
[]

$ echo '[[],{},1,"foo",null,true,false]' | jq '.[] | objects'
{}

$ echo '[[],{},1,"foo",null,true,false]' | jq '.[] | booleans'
true
false

$ echo '[[],{},1,"foo",null,true,false]' | jq '.[] | scalars'
1
"foo"
null
true
false

$ echo '[[],{},1,"foo",null,true,false]' | jq '.[] | normals'
1

$ echo '[[],{},1,"foo",null,true,false]' | jq '.[] | finites'
1

$ echo '[[],{},1,"foo",null,true,false]' | jq '.[] | nulls'
null

ويمكن استخدام any والتي تقوم بارجاع true اذا أي من عناصر المصفوفة حقق الشرط
أو all والتي تقوم بارجاع true فقط في حالة أن جميع عناصر المصفوفة حققوا الشرط

$ echo [true, false] | jq any
true

$ echo [false, false] | jq any
false

$ echo [] | jq any
false

$ echo [1, 2, 3] | jq 'any(. > 2)'
true

$ echo [1, 2, 3] | jq 'any(. > 3)'
false

تسطيح عناصر المصفوفة باستخدام flatten

اذا كانت المصفوفة تحتوي علي عناصر متداخلة يمكن جعلها flat باستخدام الدالة flatten

$ echo [1, [2], [[3]]] | jq flatten
[1,2,3]

$ echo [1, [2], [[3]]] | jq 'flatten(1)'
[1, 2, [3]]

$ echo [[]] | jq flatten
[]

ترتيب عناصر المصفوفة باستخدام sort أو sort_by

يتم ترتيب عناصر المصفوفة حسب الترتيب التالي:

  • null

  • false

  • true

  • numbers

  • strings, in alphabetical order (by Unicode codepoint value)

  • arrays, in lexical order

  • objects

بالنسبة للـ objects يتم أولا مقارنة الـ keys وفي حالة تساوي مفتاحين يتم الترتيب حسب الـ values

ويمكن استخدام sort_by للترتيب حسب مفتاح معين

يمكن لـ sort_by ان تعطي أكثر من نتيجة وفي هذه الحالة يتم الترتيب بالنتيجة الأولي وفي حالة تساوي القيم يتم الترتيب بالقيمة التالية وهكذا.

$ echo [8,3,null,6] | jq sort
[null,3,6,8]

$ echo '[{"foo":4, "bar":11}, {"foo":5, "bar":10}, {"foo":2, "bar":1}]' | jq 'sort'
[{"foo":2,"bar":1},{"foo":5,"bar":10},{"foo":4,"bar":11}]

$ echo '[{"foo":4, "bar":11}, {"foo":5, "bar":10}, {"foo":2, "bar":1}]' | jq 'sort_by(.foo)'
[{"foo":2,"bar":1},{"foo":4,"bar":11},{"foo":5,"bar":10}]

$ echo '[{"foo":4, "bar":11}, {"foo":5, "bar":10}, {"foo":2, "bar":1}]' | jq 'sort_by(.bar)'
[{"foo":2,"bar":1},{"foo":5,"bar":10},{"foo":4,"bar":11}]

$ echo '[{"foo":4, "bar":11}, {"foo":5, "bar":10}, {"foo":2, "bar":1}]' | jq 'sort_by(.bar, .foo)'
[{"foo":2,"bar":1},{"foo":5,"bar":10},{"foo":4,"bar":11}]

$ echo '[{"foo":4, "bar":11}, {"foo":5, "bar":10}, {"foo":2, "bar":1}]' | jq 'sort_by(.foo, .bar)'
[{"foo":2,"bar":1},{"foo":4,"bar":11},{"foo":5,"bar":10}]

تطبيق عملي: يمكن مثلا استخدام هذه الميزة لترتيب حقول ملف package.json

تقسيم مصفوفة الي مجموعات حسب قيمة عنصر معين باستخدام group_by

$ echo '[{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]' | jq 'group_by(.foo)'
[
  [ {"foo": 1,  "bar": 10}, {"foo": 1, "bar": 1} ], //<--- group 1 (foo=1)
  [ {"foo": 3, "bar": 100} ] //<--- group 2 9foo=3)
]

ايجاد أصغر أو أكبر قيمة

يمكن ايجاد أصغر أو أكبر قيمة باستخدام min, max, min_by(path_exp), max_by(path_exp)

$ echo '[5,4,2,7]' | jq min
2

$ echo '[5,4,2,7]' | jq max

بالنسبة للـ objects يتم الترتيب بالمفاتيح أولا ثم بالقيم

$ echo '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]' | jq min
{
  "foo": 2,
  "bar": 3
}

تم أولا عمل ترتيب داخلي لكل object

{"bar":14, "foo":1}
{"bar":3, "foo":2}

ثم مقارنة المفاتيح, وفي حالة تساوي المفاتيح يتم مقارنة القيم

وهنا نجد أن bar:3 أصغر من bar: 14 لذا يتم اختيار هذا العنصر علي أنه الاصغر وتكون النتيجة {"foo":2, "bar":3}

ويمكن تحديد حقل معين أو أكثر للترتيب عن طريقه

$ echo '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]' | jq 'min_by(.foo)'
{"foo": 1, "bar": 14}

$ echo '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]' | jq 'min_by(.bar)'
{"foo": 2, "bar": 3}

أزالة العناصر المكررة

عند استخدام unique أو uniqu_by يتم ترتيب العناصر مع ازالة العناصر المكررة

$ echo '[1,2,5,3,5,3,1,3]' | jq unique 
[1,2,3,5]

$ echo '[{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}]' | jq unique
[{"foo":1,"bar":2},{"foo":1,"bar":3},{"foo":4,"bar":5}]

$ echo '[{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}]' | jq 'unique_by(.foo)'
[{"foo":1,"bar":2},{"foo":4,"bar":5}]

$ echo '[{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}]' | jq 'unique_by(.bar)'
[{"foo":1,"bar":2},{"foo":1,"bar":3},{"foo":4,"bar":5}]

$ echo '["chunky", "bacon", "kitten", "cicada", "asparagus"]' | jq 'unique_by(length)'
["bacon","chunky","asparagus"]

اعكاس ترتيب عناصر المصفوفة باستخدام reverse

$ echo '[1,2,3,4]' | jq reverse
[4,3,2,1]

التحقق من وجود عنصر معين داخل المصفوفة باستخدام contains

$ echo '["a", "b", "c"]' | jq 'contains(["a"])'
true

$ echo '["a", "b", "c"]' | jq 'contains(["a", "b"])'
true

$ echo '["a", "b", "c"]' | jq 'contains(["a", "b", "d"])'
false

// strings

$ echo '"abcd"' | jq 'contains("ab")'
true

// objects 
// 
$ echo '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' | jq 'contains({foo: 12, bar: [{barp: 12}]})'
true

$ echo '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}' | jq 'contains({foo: 12})'
true

ويمكن اجراء العملية العكسية باستخدام inside أي التحقق أن عنصر ما يحتوي عنصر اخر, وهو عكس contains

// checking that the array includes "a"
$ echo '["a", "b", "c"]' | jq 'contains(["a"])'
true

// checking that "a" is inside the array
$ echo '["a"]' | jq 'inside(["a", "b", "c"])'
true

ويمكن ايجاد أماكن وجود العنصر داخل المصفوفة باستخدام indices

او الاكتفاء بمكان أول تواجد فقط باستخدام index أو اخر مكان باستخدام rindex

$ echo '[0,1,2,1,3,1,4]' | jq 'indices(1)'
[1,3,5]

$ echo '"abcda"' | jq 'indices("a")'
[0,4]

$ echo '[0,1,2,3,1,4,2,5,1,2,6,7]' | jq 'indices(1, 2)'
[1,4,8]
[2,6,9]

$ echo '[0,1,2,1,3,1,4]' | jq 'index(1)'
1

$ echo '[0,1,2,1,3,1,4]' | jq 'rindex(1)'
5

الحصول علي توليفات عدة مصفوفات باستخدام combinations

تخيل أن معنا مصفوفتان [1,2] و [3,4] ونريد توليف مصفوففات منها, سنأخذ كل عنصر من عناصر المصفوفة الاولي مع كل عنصر من عناصر المصفوفة الثانية في مصفوفة كالتالي

1 → 3

1 → 4

2 → 3

2 →4

وتكون النتيجة هي المصفوفات التي تشكلت معنا من هذه التوليفة

وفي حالة وجود رقم داخل cominations() فانه يتم توليد مصفوفات كل واحدة مكونة من هذا العدد من العناصر

مثلا لو عندنا مصفوفة [0,1] وقمنا بتطبيق combinations(2) عليها, يتم تكوين مجموعة من المصفوفات طول كل واحدة منهم 2 عنصر

وتكون كل الاحتمالات كالتالي:

المصفوة الاولي تتكون من العنصر الاول فقط مكرر بالعدد المطلور (2) وبالتالي يكون لدينا [0,0]

المصفوفة الثانية تكون باستبدال عنصر واحد فقط مع العنصر التالي في المصفوفة الاصلية وبالتالي يكون لدينا [0,1]

ثم تكرار الاستبدال تدريجيا بنفس المنطق حتي نصل الي مصفوفة كلها من العنصر الثاني فقط من المصفوفة الاصلية, أي [1,1]

ولفهم الفكرة أكثر, دعنا تجرب combinations(3) علي نفس المصفوفة [0,1]

نريد تكوين مجموعة مصفوفات كل واحدة منها مكونة من 3 عناصر, أول مصفوفة مكونة من العنصر الاول فقط, ثم يتم الاستبدال التدريجي مع العنصر الثاني حتي نحصل علي مصفوفة مكونة من العنصر الثاني فقط

أول مصفوفة مكونة من العنصر الاول فقط مكرر 3 مرات [0,0,0]

يتم استبدال عنصر واحد فقط بالعنصر الثاني في المصفوفة الاصلية [0,0,1]

يتم استبدال عنصر واحد فقط مرة ثانية [0,1,0] ثم [1,0,0]

ونستمر في الاستبدال حتي نصل الي [1,1,1]

$ echo [[1,2], [3, 4]] | jq 'combinations'
[1,3]
[1,4]
[2,3]
[2,4]


$ echo [0, 1] | jq 'combinations(0)'
[]

$ echo [0, 1] | jq 'combinations(1)'
[0]
[1]

$ echo [0, 1] | jq 'combinations(2)'
[0,0]
[0,1]
[1,0]
[1,1]

$ echo [0, 1] | jq 'combinations(3)'
[0,0,0]
[0,0,1]
[0,1,0]
[0,1,1]
[1,0,0]
[1,0,1]
[1,1,0]
[1,1,1]

$ echo [0, 1] | jq 'combinations(4)'
[0,0,0,0]
[0,0,0,1]
[0,0,1,0]
[0,0,1,1]
[0,1,0,0]
[0,1,0,1]
[0,1,1,0]
[0,1,1,1]
[1,0,0,0]
[1,0,0,1]
[1,0,1,0]
[1,0,1,1]
[1,1,0,0]
[1,1,0,1]
[1,1,1,0]
[1,1,1,1]

$ echo [[1,2], [3, 4]] | jq 'combinations(1)'
[[1,2]]
[[3,4]]

$ echo [[1,2], [3, 4]] | jq 'combinations(2)'
[[1,2],[1,2]]
[[1,2],[3,4]]
[[3,4],[1,2]]
[[3,4],[3,4]]

نأتي هنا للسؤال المهم, كيف نستفيد من هذه الميزة في المشاريع الحقيقية؟

في الحقيقة يوجد عدة استخدامات مفيدة جدا منها علي سبيل المثال:

في الاختبارات: نريد مثلا توليد test cases مكونة من كل الاحتمالات الممكنة, مثلا لدينا نظام تشغيل Windows ونظام Linux ونظام Mac وكذلك لدينا متصفحات مختلفة, ونريد اجراء الاختبار الخاص بنا علي كل المتصفحات في كل الانظمة, أي نريد مثلا التجربة علي متصفح Chrome علي نظام Windows و Linux و Mac ثم تجربة Firefox بنفس الطريقة ثم Edge بنفس الطريقة وهكذا

وبالتالي يمكن تطبيق شئ كالتالي

echo '[ ["Windows", "Linux", "Mac"], ["Chrome", "Firefox", "Edge"] ]' | jq 'combinations(2)'

في الـ debugging يمكن مثلا تجربة النظام بالكامل مع تفعيل توليفات مختلفة من المميزات للتحقق أن الخطأ يظهر في حالات معينة

echo '["on", "off"]' | jq 'combinations(3)'
[on,on,on] //<--- enable all features
[on,on,off] //<-- enable all features except the last one
...

ويمكن ايضا الاستفادة منها في حل الألغاز, مثلا في لعبة الشطرنج نريد حصر كل الاحتمالات الممكنة لكل قطعة, مثلا يمكن لقطعة ما التحرك في اتجاه ما أو اخر وفي كل اتجاه يمكن أن يتحرك عدد معين من الخطوات

وكذلك في البيزنس لمعرفة كل القرارات الممكنة, أو في مضاربات البورصة لمعرفة كل الاحتمالات, أو في data analysis لمعرفة كل الاحتمالات الممكنة

وفي مجال الذكاء الصناعي يمكن استخدامها في عمليات NLP preprocessing