ru
Feedback
ToCode

ToCode

Открыть в Telegram

טיפים קצרים למתכנתים מאת ינון פרק

Больше
1 421
Подписчики
Нет данных24 часа
+37 дней
-430 день
Архив постов
ToCode
1 420
# ארבעה כללים לכתיבת קוד אדיב לא רק אנשים יכולים להיות אדיבים, התואר הזה יעבוד גם על קוד אם נקפיד על מספר כללים: 1. קוד אדיב לא עושה לך עבודה. הוא לא מבקש ממך שתשדרג כל יומיים ולא מכריח אותך לשנות את הקוד שלך כל פעם ששידרגת גירסה. אם כבר הכניסו שינויים גדולים הם ישמרו על תאימות אחורה. 2. קוד אדיב יודע לעבוד קצת בשבילך. הוא לא כולל הרבה תלויות ואם כבר יש הוא יביא אותם איתו. הוא גם יגיע עם תיעוד מקיף שכולל הרבה דוגמאות שאפשר כבר לקחת ולהתחיל מהן. 3. קוד אדיב לא יתווכח כשתחליט לעשות דברים קצת אחרת ממה שהוא תכנן. הוא יתאמץ כדי לתת לך לעבוד בדרך שלך, ויבין שכל Constraint שמוסיפים למערכת רק מקשה על השימוש בו. 4. קוד אדיב יעזור לחברים. כשאתם מנסים להשתמש בו לא בדיוק כמו שצריך הוא יידע להציג הודעות שגיאה מפורטות ומדויקות ולהסביר לכם מה נשבר ומה צריך לעשות כדי לסדר את זה.

ToCode
1 420
# טיפ CSS: הסתרת פוקוס כשלוחצים על לינק עם העכבר בשביל לבנות אתרים נגישים אנחנו צריכים להוסיף אינדיקציה ויזואלית כשכפתור נכנס לפוקוס, כדי שמשתמשים יוכלו לנווט בין הכפתורים של האתר בעזרת לחיצה על Tab. הבעיה שגם לחיצה עם העכבר על כפתור מכניסה את הכפתור לפוקוס, אפילו שמשתמשים יודעים על מה הם לחצו ולא צריכים את האינדיקציה הזו. אפשר לראות את הבעיה בקודפן הבא, פשוט לחצו על הכפתור ותראו את הריבוע הכחול שמופיע סביבו: https://codepen.io/ynonp/pen/LYMrbzw <iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/ynonp/embed/LYMrbzw?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/ynonp/pen/LYMrbzw"> Untitled</a> by Ynon Perek (<a href="https://codepen.io/ynonp">@ynonp</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> מה עושים? פיתרון קל הוא להוסיף קצת JavaScript שיזהה לחיצות עכבר ויוסיף קלאס מיוחד ל body. בלחיצה על טאב נוריד את הקלאס הזה. ב CSS נציג את האינדיקציה הויזואלית לפוקוס רק אם הקלאס לא נמצא על ה body. קוד? ברור:
document.body.addEventListener("mousedown", () => {
  document.body.classList.add("using-mouse");
});
document.body.addEventListener("keydown", ({ key }) => { 
  if (key === "Tab") { 
    document.body.classList.remove("using-mouse"); 
  } 
});
ובקודפן: https://codepen.io/ynonp/pen/LYMmmEd <iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/ynonp/embed/LYMmmEd?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/ynonp/pen/LYMmmEd"> Untitled</a> by Ynon Perek (<a href="https://codepen.io/ynonp">@ynonp</a>) on <a href="https://codepen.io">CodePen</a>. </iframe>

ToCode
1 420
# ומה עוד יהיה בהרצאה? אחד הדברים המעניינים שקורים ברשת בשנים האחרונות, ובמיוחד מאז ה AI, הוא שכבר לא צריך ללכת להרצאות בשביל ללמוד דברים חדשים. אפשר להגיד שזה אפילו מיותר. כן אני יכול לראות ביוטיוב כל הרצאה מכל כנס, אבל הרבה יותר מהר לבקש מ Chat GPT לתקצר לי אותה או פשוט ללמד אותי מה שרציתי ללמוד משם. ונכון, הוא עדיין לא מושלם ויש לו הזיות, אבל אני חושב שבהסתכלות קדימה זה הולך להיפתר ודי מהר. אז אם לא צריך יותר לשבת בכיתה בקבוצה בשביל ללמוד את החומר, למה בכל זאת אנחנו מעדיפים ללמוד בצורה כזאת? מה עוד אנחנו מקבלים מהרצאה חוץ מחומר הלימוד? הנה כמה רעיונות: 1. הרצאה מרגישה בטוחה - ככה למדנו מאז שהיינו קטנים, ויש משהו מרגיע ובטוח בלהמשיך משהו שעשינו לאורך שנים מגיל צעיר. 2. בהרצאה אני מרגיש שייך לקבוצה - יש איתי עוד אנשים שלומדים את זה. אני לא לבד. 3. הרגשה שאני יודע על מה מדובר - שאני במקום הנכון. שאני מבין. זה משהו שמאוד קשה לקבל מ AI. 4. כשאני משתתף ועונה על שאלות אני מרגיש שמסתכלים עליי - אני מרגיש שלמישהו נוסף (המרצה, שאר התלמידים) אכפת מה יש לי להגיד. ההשתתפות נותנת את התחושה שיש לי מה לתרום לשיחה ומחזקת את ההבנה. 5. בהרצאה יש דינמיקה קבוצתית - גם אם רוב הזמן רק המרצה מדבר, מספיק להסתכל מסביב על זה שנרדם, ועל זאת שמתפרצת עם שאלה, ועל ההוא שעובד על המחשב באותו זמן והשניים מקדימה שמקשקשים מול הפרצוף של המרצה. אחרי שעה אני כבר מרגיש מיודד עם רובם. 6. הפתעה - הסיפורים הקטנים שלא תכננתי לשמוע ואולי שווים יותר מהנושא האמיתי. ומה אתכם? מה אתם תמשיכו לקחת מהרצאות אחרי שנוכל ללמוד הכל מה AI? למה עוד תרצו להמשיך לבוא?

ToCode
1 420
# למה לא התייאשתי אחרי הסיפור של Zecento בחור משקיע שנתיים ו 15 אלף דולר בפיתוח תוסף לכרום, ובסוף מרוויח מזה משהו כמו 200$. שוק מסוכן? אל תפתחו סטארט-אפ? עדיף להישאר בעבודה קבועה? לא יודע, בואו נקרא את הפרטים: 1. התוסף מאפשר לקבל עדכונים לווטסאפ כשמחיר של מוצר שמעניין אתכם יורד באמזון. 2. התוסף פעיל רק באיטליה 3. המוצר הוא Freemium, כלומר התוסף הוא חינמי אבל יש גירסת פרמיום שעולה כסף. מה מסתבר? רק אחוז מזערי מהמשתמשים שידרגו לגירסת הפרימיום, והבחור לא מצליח להרוויח מהמוצר שלו. לא מזמן הוא הרוויח משהו כמו 200$ מ"טיפים" דרך כפתור בסגנון Buy Me Coffee בגירסה החינמית. מסקנות: 1. יש מוצרים שיהיה קשה מאוד למכור אותם. יש גם מוצרים שיהיה קל למכור אותם, ובכל מקרה רוב המוצרים יצטרכו התאמה לקהל יעד כדי שאפשר יהיה למכור אותם (מה שקוראים Product/Market Fit). 2. לא כל מוצר חינמי טוב ושימושי שווה שידרוג, במיוחד כשה"פיצ'ר" שהוא נותן לא נראה כזה חשוב ויש אינסוף מוצרים דומים וחינמיים שעושים בדיוק אותו דבר. ואולי המסקנה הכי חשובה - אם המטרה שלכם היא לבנות מוצר שירוויח כסף (בניגוד לבניית מוצר בשביל עצמכם, בשביל תיק עבודות או כל סיבה אחרת), אז מוצרים בתשלום הם אופציה קלה בהרבה ממוצרי Freemium. כשמוצר בתשלום לא מצליח אין לקוחות ואפשר לזרוק הכל ולהתחיל מוצר חדש. מוצרי Freemium יכולים להשיג בסיס לקוחות גדול שלא משלם, ואנחנו נכנסים למערבולת של הוצאות בניסיון להבין איך לעשות מזה כסף.

ToCode
1 420
# הזדמנות שניה לא מזמן מישהו שאל ברדיט לגבי מובאקס ואם הוא עדיין רלוונטי. רוב התשובות היו של אנשים שבחרו את הכלי (מובאקס או רידאקס) לפני כמה שנים ונשארו עם הבחירה. עוד הרבה תשובות המליצו על כלים חדשים יותר כמו זוסטנד או Context. מה שהיה חסר לי באותו דיון הוא אותם אנשים שבחרו לתת הזדמנות שניה. "ניסיתי את מובאקס לפני כמה שנים, הוא לא עבד לי טוב, אבל לא מזמן ניסיתי אותו מחדש בפרויקט אחרי מספר פרויקטים עם רידאקס ואלה הדברים שאני חושב שהוא עושה טוב יותר". או "ניסיתי את רידאקס לפני כמה שנים, הכל היה שם מסובך, אבל אחרי כמה פרויקטים עם מובאקס חזרתי אליו ושמחתי למצוא את Redux Toolkit שהפך את תהליך העבודה להרבה יותר פשוט. יחד עם React Hooks נראה שזה פיתרון שאמשיך איתו גם בעתיד מהסיבות הבאות" בהתלבטות טכנולוגית בין כלים בשלים, שווה לחפש את אלה שעברו וצברו ניסיון בשתי הסביבות, ואולי אפילו למצוא פרויקטים שלהם להשוואה. הם יתנו לנו נקודת מבט הרבה יותר מעמיקה ומעניינת.

ToCode
1 420
# האתגר: להוציא החוצה את התלויות רוב הקוד שאני רואה של מערכות שעובדות עם רכיבים חיצוניים (כמו בסיס נתונים) משלב את החלק שקורא את המידע עם החלק שמעבד אותו ומסיק מסקנות. זה יכול להיות בגלל אילוצים של ORM או בגלל שאנחנו לא חושבים מספיק על הפרדות לפני שכותבים את הקוד, או שחושבים אבל פוחדים מבעיות תחזוקה. שתי דוגמאות- הקוד של swapi שמחזיר מידע על מלחמת הכוכבים כולל את המחלקה:
class PeopleViewSet(viewsets.ReadOnlyModelViewSet):

    queryset = People.objects.all()
    serializer_class = PeopleSerializer
    search_fields = ('name',)

    def retrieve(self, request, *args, **kwargs):
        return super(PeopleViewSet, self).retrieve(request, *args, **kwargs)

    def list(self, request, *args, **kwargs):
        return super(PeopleViewSet, self).list(request, *args, **kwargs)
הקוד של kutt כולל את הבלוק:
export const get = async (match: Partial<Link>, params: GetParams) => {
  const query = knex<LinkJoinedDomain>("links")
    .select(...selectable)
    .where(normalizeMatch(match))
    .offset(params.skip)
    .limit(params.limit)
    .orderBy("created_at", "desc");

  if (params.search) {
    query.andWhereRaw(
      "concat_ws(' ', description, links.address, target, domains.address) ILIKE '%' || ? || '%'",
      [params.search]
    );
  }

  query.leftJoin("domains", "links.domain_id", "domains.id");

  const links: LinkJoinedDomain[] = await query;

  return links;
};
והבעיה בתבנית היא שנוצרה לנו בעיה בבדיקות. בשביל לבדוק את הלוגיקה אנחנו חייבים בסיס נתונים כי הקוד של הלוגיקה כבר מריץ את השאילתה מול בסיס הנתונים. בשני המקרים אפשר לדמיין הפרדה בין שלושה מנגנונים: 1. הקוד שבונה את השאילתה לפי פרמטרים 2. הקוד ששולח שאילתה לבסיס נתונים ומחזיר תשובה 3. הקוד שמפענח את המידע שחזר ומארגן אותו לצורך שליחה למשתמש רק (2) צריך את הגישה לבסיס הנתונים, ורק (2) הוא הקוד שאינו ספציפי לפונקציה או אפילו למערכת מסוימת. כשמצליחים להוציא את החלק הזה ולקבל אותו מבחוץ, אנחנו לוקחים צעד ענק קדימה לכיוון קוד שקל יותר לבדוק אותו, כי כבר לא צריך בסיס נתונים בבדיקה. וכן זה נקרא Dependency Injection לדרייבר של בסיס הנתונים. וכן קשה מאוד להגיע לשם אחרי שהמערכת כתובה.

ToCode
1 420
# תוכנית ללימוד שפת תכנות חדשה (והפעם - סקאלה) פעם אחרונה שהסתכלתי על סקאלה היתה בגירסה 2 נקודה משהו. היא נראתה כמו גירסה מוזרה של Java שמנסה להיות שפת תכנות פונקציונאלית אבל פחות טובה מקלוז'ר או אליקסיר, ולא ראיתי את הטעם להמשיך. לאחרונה יצא לי להסתכל שוב על השפה, הפעם בגירסה 3, ואני לא יודע אם זה אני שהשתניתי או סקאלה אבל היא נראית הרבה יותר מוצלחת ממה שזכרתי, אז אני מתכנן לנסות שוב ללמוד סקאלה מאפס. היתרון הכי גדול של ללמוד שפה חדשה בסוף 2023 הוא כמובן כלי ה AI שהופכים את תהליך הלימוד להרבה יותר מהיר. כל פעם שאני נתקע או שמשהו לא עובד אני פשוט מדביק את הקוד שחשבתי שצריך לעבוד ל Chat GPT או לבינג ומבקש ממנו שיציע תיקון. ובגלל שאני ממש בהתחלה הוא מצליח יפה לזהות את השטויות שלי ולהציע גירסאות יותר תואמות לשפה. אני לא יודע איך ילך המסע שלי עם סקאלה וכמה זמן הוא ייקח, אבל אני אוהב בתחילת מסע לשרטט מפה של דברים שהייתי רוצה למצוא. זאת הרשימה שלי של תוכניות שנראה לי שיהיה מעניין לממש בסקאלה כדי להרגיש שאני מבין מה הולך שם. 1. מספיק סקאלה כדי שאפשר יהיה לעבוד - תחנה ראשונה תהיה לפתור את התרגילים הראשונים של פרויקט אוילר וכמה שאלות מ Advent Of Code וכל מיני אתגרי הכנה לראיונות עבודה רק בשביל לראות שאני מצליח לכתוב קוד בסיסי בשפה. אני מקווה לכל תוכנית שאכתוב להוסיף גם תוכנית בדיקה כדי להבין איך עובד מנגנון הבדיקות בסקאלה. 2. פיתוח אפליקציית ווב עם בסיס נתונים - היא לא חייבת להיות מתוחכמת, אפילו Backend לאפליקציית Todos יהיה טוב פה. המטרה היא לראות איך מתקינים חבילות חיצוניות, איזה אפשרויות קיימות לפיתוח ווב וחיבור לבסיסי נתונים ואיך נראית תוכנית קצת יותר גדולה בסקאלה. 3. חיבור לבסיס נתונים neo4j עם טיפוסים - לא אשקר, אחת הסיבות המרכזיות ללמוד סקאלה היא העבודה בתוך ה JVM. בגלל שאני כבר מכיר neo4j ואוהב לעבוד איתו, הייתי רוצה לראות אם הדרייברים של סקאלה ומערכת הטיפוסים שלה יאפשרו לי לעבוד בצורה יותר בטוחה עם בסיס נתונים זה. 4. פיתוח אפליקציית GUI - אין לי מושג איך בונים ממשק גרפי בסקאלה, ואני מקווה שיש משהו יותר מוצלח מסווינג. בכל מקרה זו תחנה שאשמח לבקר בה. משחק איקס עיגול או סנייק יהיו מועמדים טובים לתחנה זו. 5. יש שמועה שסקאלה מתקמפלת ל Web Assembly, וזה בהחלט כיוון שחשוב לחקור אותו היום. וכן יש גם את scala-js אז צריך לראות איזו אפשרות תעבוד טוב יותר. הקוד עצמו יכול להיות פרונט אנד לאפליקציית הווב בסקאלה שתכננתי לכתוב, או משחקון צד-לקוח בלבד. 6. תחנה אחרונה ללימוד סקאלה תהיה פיתוח משהו אמיתי שאני צריך, והדבר הראשון שקופץ לראש הוא בוט לסלאק. כן השמות דומים וגם הרבה זמן רציתי בוט לסלאק וספריה כמו ScalaBot נראית כמו רעיון מאוד אטרקטיבי שאולי יחליף גם את בוט הטלגרם הפייתונאי שלי. יודעים כבר סקאלה ורוצים להציע תחנות נוספות למסלול? אל תתביישו להשאיר תגובה. וכמובן תרגישו חופשי להשתמש במסלול הזה כבסיס בפעם הבאה שאתם רוצים ללמוד שפת תכנות כלשהי.

ToCode
1 420
# אף פעם לא קרה לי כשמישהו מקטר על בעיה בכלי או שיטת עבודה מסוימת שאנחנו אוהבים, מאוד מפתה לנפנף אותו עם איזה "לי זה אף פעם לא קרה" או "אולי אתה עובד עם גירסה ישנה". גישה כזאת לא עוזרת לא לחבר ולא לנו. החבר יקבל את הרושם שאולי הכלי הזה לא בשבילו, ואנחנו נפסיד הזדמנות ללמידה. סקרנות היא גישה טובה יותר. בוא תראה לי, באיזה גירסאות אתה משתמש? יש לך פרויקט דוגמה קטן? למה בחרת להשתמש בכלי בצורה הזאת? לפעמים כשיהיה לי מספיק מידע על הבעיה אני אוכל מיד לעזור לחבר להבין את הכלי, משהו כמו "בשביל לקבל תוצאות טובות עם Micro Services מאוד עוזר לוודא שאפשר להעלות גירסה בסרביס אחד בלי להשפיע על הסרביסים האחרים. תראה פה אם תארגן מחדש את הסרביסים תוכל לבטל את התלות". במקרים אחרים השאלה של החבר משאירה אותי מופתע. בסיפור של המייקרו סרביסס אני יכול להסתכל על הארכיטקטורה והאתגרים ואולי לא יהיה לי רעיון מיידי איך לפתור את הפלונטר, או אפילו למה דברים נשברו. פה אני מזהה הזדמנות ללמידה. שווה להוסיף שעם כל האהבה לכלי AI, הם לא תמיד יעילים במצבים האלה. הם לא יודעים מספיק טוב לתשאל והרבה פעמים יציעו פיתרון לבעיה שאתם מתארים, במקום לעזור לכם לראות את הבעיה בצורה נכונה. בעבודה איתם במקום לתאר את הבעיה נסו לשאול מהם ה Best Practices וה Common Pitfalls בשימוש בכלי. וכן גם חיפוש כללי יותר בגוגל יכול לעזור.

ToCode
1 420
$ curl --silent https://api.github.com/users/ynonp/repos | jq '[.[] | select(.stargazers_count > 0) | {"name": .name, "stars": .stargazers_count}] | sort_by(.stars) | reverse'

[
  {
    "name": "Adv-Perl-Examples",
    "stars": 4
  },
  {
    "name": "catalyst-talk-examples",
    "stars": 2
  },
  {
    "name": "basic-perl-examples",
    "stars": 2
  },
  {
    "name": "capybara-rspec-demo",
    "stars": 1
  },
  {
    "name": "bootstrap4-webinar-demos",
    "stars": 1
  },
  {
    "name": "Basic-Perl-July-24",
    "stars": 1
  }
]
## עדכון קובץ tsconfig.json מקומי יכולת אחרונה של jq שאני רוצה להראות במדריך זה היא השימוש בו כדי לעדכן קבצי json קיימים על הדיסק. לדוגמה נניח שיש לכם קובץ tsconfig.json עם התוכן הבא:
$ cat tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist",
    "sourceMap": true
  },
  "include": [
    "./src/**/*"
  ]
}
אופרטור ההשמה של jq מאפשר להדפיס ערך חדש למפתח קיים, או ליצור מפתח חדש. בצד שמאל של סימן ה = אני כותב את המפתח ובצד ימין את הערך החדש שלו. בדוגמה שלנו אני יכול לשנות את ה target למשהו יותר מודרני:
$ jq '.compilerOptions.target = "esnext"' < tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist",
    "sourceMap": true
  },
  "include": [
    "./src/**/*"
  ]
}
אפשר להוסיף מפתחות חדשים לגמרי:
$ jq '.compilerOptions.target = "esnext" | .watchOptions.excludeFiles = ["temp/file.ts"]' < tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist",
    "sourceMap": true
  },
  "include": [
    "./src/**/*"
  ],
  "watchOptions": {
    "excludeFiles": [
      "temp/file.ts"
    ]
  }
}
ואפשר למחוק מפתח:
jq '.compilerOptions.target = "esnext" | .watchOptions.excludeFiles = ["temp/file.ts"] | del(.include)' < tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist",
    "sourceMap": true
  },
  "watchOptions": {
    "excludeFiles": [
      "temp/file.ts"
    ]
  }
}
## סיכום הכלי jq מציע אינסוף דרכים לקרוא ולכתוב מידע בפורמט JSON ואני מקווה שהראיתי לכם מספיק ממנו כדי להשאיר טעם של עוד. בשביל ללמוד יותר אני ממליץ לקרוא את דף התיעוד המצוין על הכלי בעזרת man jq או בקישור: https://jqlang.github.io/jq/manual/ כמו תמיד עם כלים השימוש בהם בהתחלה מאתגר ולא תמיד זוכרים את כל הדברים שראיתם בוידאו, אבל ככל שתעבדו איתו יותר תראו שאתם מצליחים לכתוב אוטומציות מהירות על JSON-ים ו jq הופך לסוג של כח על בארגז הכלים היוניקסאי שלכם.

ToCode
1 420
"name": "capybara-rspec-demo",
  "stargazers_count": 1
}
{
  "name": "catalyst-talk-examples",
  "stargazers_count": 2
}
אנחנו מתקרבים. יש לנו רשימה של אוביקטים עם name ו stargazers_count, אבל אנחנו צריכים שכל אוביקט יכיל רק מפתח אחד, שזה מה שכתוב ב name שלו, והערך של אותו מפתח יהיה מספר הכוכבים (וכן אנחנו גם רוצים את כולם באוביקט אחד, אבל רגע עם זה). בתוך הסוגריים המסולסלים אני יכול לבחור שם למפתחות:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '.[] | select(.stargazers_count > 0) | {"repo": .name, "stars": .stargazers_count}'
{
  "repo": "Adv-Perl-Examples",
  "stars": 4
}
{
  "repo": "basic-perl-examples",
  "stars": 2
}
{
  "repo": "Basic-Perl-July-24",
  "stars": 1
}
{
  "repo": "bootstrap4-webinar-demos",
  "stars": 1
}
{
  "repo": "capybara-rspec-demo",
  "stars": 1
}
{
  "repo": "catalyst-talk-examples",
  "stars": 2
}
ואם אני משתמש בסוגריים מסביב למפתח אני יכול גם להשתמש בביטוי מתוך ה JSONבתור שם המפתח, וזה נראה ככה:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '.[] | select(.stargazers_count > 0) | {(.name): .stargazers_count}'

{
  "Adv-Perl-Examples": 4
}
{
  "basic-perl-examples": 2
}
{
  "Basic-Perl-July-24": 1
}
{
  "bootstrap4-webinar-demos": 1
}
{
  "capybara-rspec-demo": 1
}
{
  "catalyst-talk-examples": 2
}
קיבלתי רשימה של אוביקטים שרציתי ואני עכשיו צריך למזג אותם. הפילטר add של jq יוכל לעזור - פילטר זה מקבל מערך של אוביקטים (או של דברים אחרים), ומחבר אותם. אם הוא קיבל מערך של מספרים הוא יסכום את כולם, מערך של מחרוזות יחזיר שרשור של כל המחרוזות ומערך של אוביקטים יחזיר אוביקט ממוזג עם כל המפתחות. אבל קודם צריך להקיף את הביטוי שלנו בסוגריים מרובעים כדי לקבל מערך במקום רשימה של אוביקטים:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '[.[] | select(.stargazers_count > 0) | {(.name): .stargazers_count}]'
[
  {
    "Adv-Perl-Examples": 4
  },
  {
    "basic-perl-examples": 2
  },
  {
    "Basic-Perl-July-24": 1
  },
  {
    "bootstrap4-webinar-demos": 1
  },
  {
    "capybara-rspec-demo": 1
  },
  {
    "catalyst-talk-examples": 2
  }
]
את זה אני כבר יכול לשלוח ל add ולקבל:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '[.[] | select(.stargazers_count > 0) | {(.name): .stargazers_count}] | add'
{
  "Adv-Perl-Examples": 4,
  "basic-perl-examples": 2,
  "Basic-Perl-July-24": 1,
  "bootstrap4-webinar-demos": 1,
  "capybara-rspec-demo": 1,
  "catalyst-talk-examples": 2
}
נ.ב. לפעמים המטרה שלנו היא לא להגיע לאוביקט אלא למערך של אוביקטים עם name ו stars, כלומר:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '[.[] | select(.stargazers_count > 0) | {"name": .name, "stars": .stargazers_count}]'

[
  {
    "name": "Adv-Perl-Examples",
    "stars": 4
  },
  {
    "name": "basic-perl-examples",
    "stars": 2
  },
  {
    "name": "Basic-Perl-July-24",
    "stars": 1
  },
  {
    "name": "bootstrap4-webinar-demos",
    "stars": 1
  },
  {
    "name": "capybara-rspec-demo",
    "stars": 1
  },
  {
    "name": "catalyst-talk-examples",
    "stars": 2
  }
]
במצב כזה סיכוי טוב שנרצה למיין את המערך לפי ה stars של כל אוביקט. וכמו שניחשתם יש ל jq פילטר בשם sort ועוד אחד בשם sort_by. זה עובד ככה:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '[.[] | select(.stargazers_count > 0) | {"name": .name, "stars": .stargazers_count}] | sort_by(.stars)'

[
  {
    "name": "Basic-Perl-July-24",
    "stars": 1
  },
  {
    "name": "bootstrap4-webinar-demos",
    "stars": 1
  },
  {
    "name": "capybara-rspec-demo",
    "stars": 1
  },
  {
    "name": "basic-perl-examples",
    "stars": 2
  },
  {
    "name": "catalyst-talk-examples",
    "stars": 2
  },
  {
    "name": "Adv-Perl-Examples",
    "stars": 4
  }
]
מעדיפים בסדר יורד? פשוט תהפכו את המערך:

ToCode
1 420
"subscribers_url": "https://api.github.com/repos/ynonp/activestorage-demo/subscribers",
  "subscription_url": "https://api.github.com/repos/ynonp/activestorage-demo/subscription",
  "commits_url": "https://api.github.com/repos/ynonp/activestorage-demo/commits{/sha}",
  "git_commits_url": "https://api.github.com/repos/ynonp/activestorage-demo/git/commits{/sha}",
  "comments_url": "https://api.github.com/repos/ynonp/activestorage-demo/comments{/number}",
  "issue_comment_url": "https://api.github.com/repos/ynonp/activestorage-demo/issues/comments{/number}",
  "contents_url": "https://api.github.com/repos/ynonp/activestorage-demo/contents/{+path}",
  "compare_url": "https://api.github.com/repos/ynonp/activestorage-demo/compare/{base}...{head}",
  "merges_url": "https://api.github.com/repos/ynonp/activestorage-demo/merges",
  "archive_url": "https://api.github.com/repos/ynonp/activestorage-demo/{archive_format}{/ref}",
  "downloads_url": "https://api.github.com/repos/ynonp/activestorage-demo/downloads",
  "issues_url": "https://api.github.com/repos/ynonp/activestorage-demo/issues{/number}",
  "pulls_url": "https://api.github.com/repos/ynonp/activestorage-demo/pulls{/number}",
  "milestones_url": "https://api.github.com/repos/ynonp/activestorage-demo/milestones{/number}",
  "notifications_url": "https://api.github.com/repos/ynonp/activestorage-demo/notifications{?since,all,participating}",
  "labels_url": "https://api.github.com/repos/ynonp/activestorage-demo/labels{/name}",
  "releases_url": "https://api.github.com/repos/ynonp/activestorage-demo/releases{/id}",
  "deployments_url": "https://api.github.com/repos/ynonp/activestorage-demo/deployments",
  "created_at": "2021-01-21T15:32:07Z",
  "updated_at": "2021-01-21T15:32:37Z",
  "pushed_at": "2021-01-21T15:32:33Z",
  "git_url": "git://github.com/ynonp/activestorage-demo.git",
  "ssh_url": "git@github.com:ynonp/activestorage-demo.git",
  "clone_url": "https://github.com/ynonp/activestorage-demo.git",
  "svn_url": "https://github.com/ynonp/activestorage-demo",
  "homepage": null,
  "size": 156,
  "stargazers_count": 0,
  "watchers_count": 0,
  "language": "Ruby",
  "has_issues": true,
  "has_projects": true,
  "has_downloads": true,
  "has_wiki": true,
  "has_pages": false,
  "has_discussions": false,
  "forks_count": 0,
  "mirror_url": null,
  "archived": false,
  "disabled": false,
  "open_issues_count": 0,
  "license": null,
  "allow_forking": true,
  "is_template": false,
  "web_commit_signoff_required": false,
  "topics": [],
  "visibility": "public",
  "forks": 0,
  "open_issues": 0,
  "watchers": 0,
  "default_branch": "main"
}
או אפילו יותר מעניין, רק את המאגרים שמספר הכוכבים שלהם גדול מאפס:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '.[] | select(.stargazers_count > 0)'
וכמובן אין טעם לראות את כל פרטי המאגר, מספיק לי השם שלו ומספר הכוכבים שקיבל:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '.[] | select(.stargazers_count > 0) | .name,.stargazers_count'

"Adv-Perl-Examples"
4
"basic-perl-examples"
2
"Basic-Perl-July-24"
1
"bootstrap4-webinar-demos"
1
"capybara-rspec-demo"
1
"catalyst-talk-examples"
2
עכשיו אני יודע אתם יודעים לקרוא פלט כזה וגם אני יודע, אבל בינינו לא היה יותר נעים לקבל את הפלט גם בתור JSON, בו המפתח יהיה שם המאגר והערך יהיה מספר הכוכבים שאותו מאגר קיבל? ברור שכן ו jq ישמח לעזור לנו גם בזה: בשביל לקבל מערך או אוביקט JSON, עלינו לעטוף את הביטוי בסוגריים מרובעים (עבור מערך) או סוגריים מסולסלים עבור אוביקט JSON. במקרה שלי אני רוצה אוביקט JSON אז אכתוב:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '.[] | select(.stargazers_count > 0) | {"name", "stargazers_count"}'

{
  "name": "Adv-Perl-Examples",
  "stargazers_count": 4
}
{
  "name": "basic-perl-examples",
  "stargazers_count": 2
}
{
  "name": "Basic-Perl-July-24",
  "stargazers_count": 1
}
{
  "name": "bootstrap4-webinar-demos",
  "stargazers_count": 1
}
{

ToCode
1 420
"allow_forking": true,
  "is_template": false,
  "web_commit_signoff_required": false,
  "topics": [],
  "visibility": "public",
  "forks": 0,
  "open_issues": 0,
  "watchers": 0,
  "default_branch": "master"
}

ואגב אם אתם צריכים עוד כמה מאגרים תמיד אפשר למשוך מספר ערכים ממערך, למשל הפקודה הבאה תדפיס את הפריטים השני והשלישי מתוך מערך המאגרים שלי:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '.[1:3]'
אבל רגע אם אנחנו כבר מציגים מספר מאגרים, למה שלא נבחר את המאגרים המעניינים? וזו גם הזדמנות ללמוד פילטר נוסף של jq. הפילטר select מקבל ביטוי ורשימה של פריטים, ומדפיס רק את הפריטים עבורם הביטוי מחזיר ערך אמת. כרגע יש לי בקלט רק מערך אחד של אוביקטים, אז אני צריך לפרק אותו ולקחת את כל האוביקטים כפריטים בודדים, לשלוח אותם ל select כדי שיחפש דברים שמעניינים אותי ואז אוכל להדפיס רק את המאגרים שרציתי. למשל נוכל למצוא ולהדפיס רק את המאגר ששמו activestorage-demo:
$ curl --silent https://api.github.com/users/ynonp/repos | jq '.[] | select(.name == "activestorage-demo")'

{
  "id": 331670960,
  "node_id": "MDEwOlJlcG9zaXRvcnkzMzE2NzA5NjA=",
  "name": "activestorage-demo",
  "full_name": "ynonp/activestorage-demo",
  "private": false,
  "owner": {
    "login": "ynonp",
    "id": 128594,
    "node_id": "MDQ6VXNlcjEyODU5NA==",
    "avatar_url": "https://avatars.githubusercontent.com/u/128594?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/ynonp",
    "html_url": "https://github.com/ynonp",
    "followers_url": "https://api.github.com/users/ynonp/followers",
    "following_url": "https://api.github.com/users/ynonp/following{/other_user}",
    "gists_url": "https://api.github.com/users/ynonp/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/ynonp/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/ynonp/subscriptions",
    "organizations_url": "https://api.github.com/users/ynonp/orgs",
    "repos_url": "https://api.github.com/users/ynonp/repos",
    "events_url": "https://api.github.com/users/ynonp/events{/privacy}",
    "received_events_url": "https://api.github.com/users/ynonp/received_events",
    "type": "User",
    "site_admin": false
  },
  "html_url": "https://github.com/ynonp/activestorage-demo",
  "description": null,
  "fork": false,
  "url": "https://api.github.com/repos/ynonp/activestorage-demo",
  "forks_url": "https://api.github.com/repos/ynonp/activestorage-demo/forks",
  "keys_url": "https://api.github.com/repos/ynonp/activestorage-demo/keys{/key_id}",
  "collaborators_url": "https://api.github.com/repos/ynonp/activestorage-demo/collaborators{/collaborator}",
  "teams_url": "https://api.github.com/repos/ynonp/activestorage-demo/teams",
  "hooks_url": "https://api.github.com/repos/ynonp/activestorage-demo/hooks",
  "issue_events_url": "https://api.github.com/repos/ynonp/activestorage-demo/issues/events{/number}",
  "events_url": "https://api.github.com/repos/ynonp/activestorage-demo/events",
  "assignees_url": "https://api.github.com/repos/ynonp/activestorage-demo/assignees{/user}",
  "branches_url": "https://api.github.com/repos/ynonp/activestorage-demo/branches{/branch}",
  "tags_url": "https://api.github.com/repos/ynonp/activestorage-demo/tags",
  "blobs_url": "https://api.github.com/repos/ynonp/activestorage-demo/git/blobs{/sha}",
  "git_tags_url": "https://api.github.com/repos/ynonp/activestorage-demo/git/tags{/sha}",
  "git_refs_url": "https://api.github.com/repos/ynonp/activestorage-demo/git/refs{/sha}",
  "trees_url": "https://api.github.com/repos/ynonp/activestorage-demo/git/trees{/sha}",
  "statuses_url": "https://api.github.com/repos/ynonp/activestorage-demo/statuses/{sha}",
  "languages_url": "https://api.github.com/repos/ynonp/activestorage-demo/languages",
  "stargazers_url": "https://api.github.com/repos/ynonp/activestorage-demo/stargazers",
  "contributors_url": "https://api.github.com/repos/ynonp/activestorage-demo/contributors",