ToCode
Відкрити в Telegram
1 419
Підписники
Немає даних24 години
-17 днів
+230 день
Архів дописів
1 419
# הזדמנויות ומתי לנצל אותן
כל יום בעבודה אנחנו מוצפים באינספור הזדמנויות למידה: אולי זה כלי חדש שמישהו כתב עליו, אולי אנחנו מסתכלים על קוד לא מספיק טוב וחושבים איך לשפר אותו, אולי בעיה שאפשר לעשות לה אוטומציה בעזרת כתיבת כלי קטן, אולי אפשר לכתוב בדיקה אוטומטית ראשונה לקוד שאין לו בדיקות בכלל. רוב הזמן ההזדמנויות האלה נשארות לא מנוצלות.
טכניקה טובה שעוזרת לנצל יותר הזדמנויות היא לשריין "זמן ריק" ביומן, ספציפית לניצול הזדמנויות.
עכשיו אני יודע מה אתם חושבים - מאיפה שיהיה לי זמן ריק בלו"ז לשריין? הרי בקושי יש לי זמן לדברים שאני צריך לעשות, וגם ככה המערכת במצב פח.
והתשובה לצערי היא שזמן לא עובד ככה.
הדברים שבפועל עשית הם תוצאה של מאבק על תשומת לב בין כל המשימות שאתה צריך לעשות, ויש משימות יותר חזקות מאחרות. באג בפרודקשן מנצח הרבה משימות אחרות, בדיקת סטטוס ברשת החברתית זו גם משימה חזקה אבל למידה או Refactoring אלה משימות מאוד חלשות.
הדרך היחידה שלנו להצליח גם ללמוד ולהשתפר היא לקחת צד במאבק המוזר הזה על הזמן שלנו, ולתת ייתרון לא הוגן למשימות מסוימות - משימות שאנחנו יודעים שנמצאות תמיד בנחיתות. הבאגים בפרודקשן יודעים לדאוג לעצמם. המשימה שלכם היא לדאוג לכל אותן משימות שלא כל כך מתחשק לעשות, אבל אחרי שתהיו טובים בהן ישפרו לכם את החיים בצורה משמעותית.
1 419
# חמישה דברים שאהבתי ב Vite ואחד שממש לא
הכלי vite מציע אוסף של תבניות וסקריפטים להקמה ותחזוקה של יישומי Front End. פיתח אותו איוון יו בשביל להציע חווית פיתוח טובה יותר למפתחי ווב בדגש על מהירות.
בעזרת Vite תוכלו לבנות פרויקטים של הפריימוורקס המובילים - Vue, React, Svelte, preact ועוד אחד שנקרא lit-element, וכמובן פרויקט וונילה ללא פריימוורק. בכל אחד מהמקרים תקבלו תבנית לפרויקט וסקריפטים להגשה במצב פיתוח או בניה במצב פרודקשן.
הנה 5 דברים שאהבתי ואחד שלא כל כך בפרויקטי Vite שיצרתי:
## השם
כי חייבים לחייך כשאיוון יו מוצא שפה שבה המילה "מהירות" מתחילה באות V והפועל "להאט" מתחיל באות R.
## חווית הפיתוח
כן, זה מהיר. קודם כל כי יש הרבה פחות תלויות אז הקמת פרויקט ריאקט לקחה לי 15 (בהשוואה ל create-react-app שביום טוב מסיים תבנית בשתי דקות). גם הרצת הפרויקט וזיהוי השינויים בזמן אמת עובדים הרבה יותר מהר מפרויקטים של create-react-app.
הסיבה המרכזית למהירות לטענת איוון יו היא המעבר לשימוש ב ES Modules במצב פיתוח, כלומר הוא לא טורח לרוץ על כל הקבצים ולבנות באנדל במצב פיתוח ובמקום זה משתמש בתמיכה הטבעית בדפדפנים ב import. זה עובד גם מבחינת זמן עליה מאוד מהיר של השרת וגם מבחינת זיהוי שינויים וטעינה מחדש מיידית של הקוד העדכני.
## אפס קונפיגורציה
הגישה של create-react-app לאפס קונפיגורציה היא איומה: הם משתמשים ב webpack אבל לא נותנים לכם לגעת בקונפיגורציות וובפאק שלהם. הם מחזיקים מערך אינסופי של Build Scripts ששוב אי אפשר לגעת בו, ואם תעזו לחלום על משהו שהם לא תומכים בו הם יצעקו עליכם ויזרקו עליכם מאות שורות של קבצי הגדרות שתתחזקו בעצמכם.
איוון יו החליף את וובפאק ב Rollup, לטענתו משיקולי מהירות, אבל הדבר החשוב הוא שחוץ מערכי ברירת מחדל מעולים הוא גם נותן לנו גישה להגדרות ה Rollup ואפילו מתעד את זה. רוצים ליצור פרויקט עם מספר קבצי HTML? אין בעיה. רוצים לשנות את מבנה קובץ ה HTML שנוצר? הכי קל בעולם, הוא חלק מהפרויקט.
וכמובן TypeScript ו Scss נתמכים Out Of The Box - פשוט טוענים קובץ עם הסיומת המתאימה והכל עובד.
נכון, אנחנו מחויבים למבנה של ES Modules ומזה לא הצלחתי לברוח - אבל במאזן הכללי של הדברים נראה לי שאיוון יו עשה עבודה די טובה. כל עוד אתם מוכנים לסמוך על התמיכה המובנית בדפדפנים ב ES6 Modules פרויקט vite יעבוד לכם די טוב.
(ואם אתם כן צריכים תמיכה בדפדפנים ישנים יש להם Legacy Plugin שנראה שנותן פיתרון).
## חיסול אוטומטי של פונקציות מתות
גם בלי קונפיגורציה הדברים החשובים שם. כשניסיתי ליצור קובץ utils.js שמייצא שתי פונקציות:
export function foo() {
return "foo";
}
export function bar() {
return "bar";
}
אבל בקובץ ה main.js טענתי רק אחת מהן:
import { foo } from './utils';
כצפוי הפונקציה bar אותה לא טענתי לא נכנסה לבאנדל שבניתי. הוא גם מספיק חכם בשביל לייצר קובץ vendor.js לספריות מ node_modules ולהוסיף Hash לשם הקובץ כדי להתמודד עם Browser Cache.
## פרויקט ריאקט בלי reportWebVitals
רק עד לפה היה מספיק לי בשביל להתחיל להשתמש ב vite לכל פרויקט ריאקט חדש שאני בונה - אבל אז גיליתי את הממתק האחרון: פרויקטי ריאקט שתבנו עם vite לא יכלול את הספריה web-vitals ולא יפעיל את reportWebVitals באופן אוטומטי. לא שיש לי משהו נגדם, פשוט מעדיף שתבניות פרויקט יכללו רק פיצ'רים שאני באמת מבקש.
## ורמאות אחת: זמן בניה לפרודקשן
למרות כל הדברים הטובים איוון יו הוסיף גם טריק מלוכלך אחד ל Vite: הוא ביטול יצירת Source Maps במצב פרודקשן. המטרה היתה כנראה לשפר מהירות בניה לפרודקשן והמחשבה היתה שממילא אף אחד לא ישים לב, ונכון די קל להחזיר את זה (בסך הכל שינוי הגדרה בקובץ הקונפיגורציה). ועדיין.
ההבדל בין Source Maps ל reportWebVitals הוא שב Source Maps אנחנו באמת משתמשים בפרודקשן. כשיש באג בפרודקשן שלא משתחזר בפיתוח, מאוד עוזר לפתוח את כלי הפיתוח ולראות את הקוד המקורי שכתבת.
בסך הכל ויט לגמרי שווה את הזמן שלכם - זמן הלימוד קצר וחווית הפיתוח הרבה יותר טובה מ create-react-app. לפרטים נוספים תוכלו לבקר באתר שלהם בקישור:
https://vitejs.dev/1 419
# לא לדאוג, אני יודע רובי
החידה הבאה הגיעה אליי במקרה בטלגרם:
> מבלי לשנות את סדר המספרים, עליכם להציב 3 סימני חיבור או חיסור ביניהם כך שתקבלו משוואה נכונה:
> 100 = 9 8 7 6 5 4 3 2 1
אחרי כמה דקות של Brute Force בראש הבנתי שעדיף לתת למחשב לשבור את הראש על זה, וזה הזכיר לי כמה נעים לכתוב קוד ברובי.
## איך מחשב ניגש לכזאת שאלה
נוח להתחיל לחשוב על החידה הזאת מהסוף. הפיתרון בטוח יהיה רצף של 4 מספרים (כי צריך 3 סימני חיבור וחיסור ביניהם). דבר נוסף שאפשר לראות הוא שאם יש לכם ביד איזשהו ניחוש חלקי, למשל התחלתם עם המספר 12, אז קל לראות מה יכולים להיות הצעדים הבאים. במקרה של 12 אפשר להמשיך להאריך את המספר ל 123, או להתחיל מספר חדש שיהיה 3 או
-3.
בכתיב מונחה עצמים נוכל לכתוב מחלקה שמייצגת שרשרת של מספרים ותהיה לה פונקציה שמחזירה את האפשרויות לצעד הבא. נניח שתהיה לי את השרשרת:
[1, 234]
כלומר פלוס אחד ואז פלוס 234, אז יש בסך הכל 3 אפשרויות לצעד הבא:
[1, 234, -5]
[1, 234, 5]
[1, 2345]
וכל שרשרת מה-3 גם יכולה להמשיך ולהיפתח בתורה ל-3 שרשראות נוספות וכך הלאה, כאשר כל שלב במקרה הגרוע יכפיל פי 3 את מספר השרשראות אבל מהר מאוד זה ייגמר כשנסיים את כל המספרים.
## והקוד ברובי
שרשרת תהיה בסך הכל סוג-של מערך שיש לו פונקציית expand שמחזירה את שלוש השרשראות שיכולות להמשיך אותה. אני מגדיר את המחלקה ומספר פוקנציות עזר:
class Chain
attr_accessor :values
def initialize(values)
@values = values
end
def last
@values[-1] || 0
end
def sum
@values.sum
end
def to_s
"Chain: #{values}"
end
end
והפונקציה המעניינת, expand, היא זו שלוקחת שרשרת ומוסיפה לה סיפרה, או בתור סיפרה חדשה למספר האחרון בשרשרת או בתור מספר חדש:
def expand
return if last.abs % 10 == 9
return if values.length > 4
value = last.abs
next_value = (value % 10) + 1
concatenated_value = (value * 10 + value % 10 + 1) * (last >= 0 ? 1 : -1)
[
Chain.new([*values, next_value]),
Chain.new([*values, -1 * next_value]),
Chain.new(values[0..-2].concat([concatenated_value]))
]
end
בעיקרון רובוקופ כעס עליי שהפונקציה מסובכת מדי, אז אם יש לכם רעיונות Refactoring שיהפכו אותה לפשוטה יותר ועדיין קריאה מוזמנים להשאיר בתגובות
ואחרי כל ההשקעה במחלקה Chain אפשר לסיים את התרגיל עם לולאה פשוטה שמתחילה עם מערך של Chain יחיד וריק, ובכל איטרציה מפעילה expand על כל השרשראות במערך ומחפשת אם הסכום של אחת מהן הוא 100:
state = [Chain.new([])]
loop do
state = state.flat_map(&:expand).reject(&:nil?)
if (final = state.find { |chain| chain.values.size == 4 && chain.sum == 100 })
puts final
break
end
end
כל התוכנית לקחה פחות מ 50 שורות קוד והרבה פחות זמן ממה שהיה לוקח לי למצוא את השרשרת המתאימה ב Brute Force בראש.1 419
# אז מה באמת ההבדל בין Authorization ל Authentication?
שתי מילים ארוכות באנגלית שמתיחסות לאותו עולם תוכן אבל לכל אחת משמעות ייחודית משלה, ולא כדאי להתבלבל.
שתי המילים מתיחסות לניהול הרשאות במערכות ווב. למערכת ווב יש טבלה של משתמשים שיכולים להתחבר, ולכל משתמש בדרך כלל יהיה מזהה ולפעמים בעמודות נוספות עוד קצת מידע על אותו משתמש.
המילה הראשונה, Authentication, או בקיצור AuthN (כי אם אפשר לבלבל קצת אז למה לא), מתיחסת ליכולת של מערכת לזהות - עבור בקשה ספציפית - מי המשתמש שיזם בקשה זו. או במילים פשוטות מי המשתמש שכרגע מנסה לעשות משהו במערכת. וכשאני אומר "מי" אני מתכוון לשורה המתאימה מטבלת המשתמשים או למזהה המשתמש ששמור שם. כל הטכניקות של זיהוי דו-שלבי, זיהוי דרך סמס, זיהוי עם טביעת אצבע או מפתחות - כולן נכנסות לתוך העולם של Authentication. את ה Authentication גם קל יחסית להוציא החוצה לסרביס או מערכת חיצוניים, לדוגמה כשתנסו להתחבר לאיזושהי מערכת של גוגל קודם תצטרכו לבצע הזדהות מול accounts.google.com כדי לוודא "מי" אתם.
המילה השניה, Authorization, או בקיצור AuthZ, מתיחסת למה מותר או אסור למשתמש לעשות. במילים אחרות בהינתן שעכשיו יושב מולי משתמש ויש לי עליו את כל המידע מטבלת המשתמשים - האם מותר או אסור לו לבצע את הפעולה שהוא רוצה לעשות.
כללי Authorization יכולים להיות מובלעים בקוד או מפורשים. כללים מובלעים הם פשוט תוצאה של הקוד שאנחנו כותבים. לדוגמה קוד מהסוג הזה כולל כללי Authorization מובלעים:
exports.getTasks = function(user) {
mongoClient.collection('tasks').find({ owner: user }).toArray();
}
הפונקציה מקבלת מזהה משתמש ומחזירה את כל המשימות של אותו משתמש, ולכן היא אומרת משהו על מה מותר או אסור למשתמשים מסוימים לעשות. אבל היא אומרת את זה בצורה מובלעת - ויחסית קל בשינוי עתידי של הקוד בטעות לבטל את הבדיקה.
כללי Authorization יכולים להיות גם מפורשים, לדוגמה את אותה פונקציה יכלתי לכתוב כך:
exports.getTasks = function(user) {
const tasks = mongoClient.collection('tasks').find({ owner: user }).toArray();
authorize(user, 'read', tasks);
return tasks;
}
את הפונקציה authorize נצטרך לכתוב במקום אחר בצורה מסודרת, ויש לא מעט פריימוורקים שיעזרו בזה. הנקודה החשובה כאן היא שגישה זו הופכת את getTasks, ואת הקוד באופן כללי, להרבה יותר מאובטח. גם אם בעתיד מישהו בטעות ישנה את השאילתה, עדיין לפני שנחזיר את המידע אנחנו מוודאים שלמשתמש מותר בכלל לקרוא אותו.
ב Node.JS, הספריה passport מספקת דרך פופולרית לבצע AuthN. הספריה cancan מספקת פיתרון Authorization ליישומי ווב, אבל היא הרבה פחות פופולרית מ Passport. אולי כי הרבה אנשים מעדיפים לבנות פיתרון כזה בעצמם.1 419
# לא נחשב
"הוא היה פרילאנסר 5 שנים, זה לא נחשב ניסיון"
"היא כתבה 8 שנות ניסיון, אבל תכל'ס רק 2 מתוכן בחברה רצינית. השאר לא נחשב"
"פחות מ-500 מילים? זה לא נחשב פוסט"
"קורס פייתון בלי פנדס? זה לא נחשב"
"כל הפרונטאנד הזה לא באמת נחשב תכנות"
"כתבת כלי ש 90% מהמתכנתים בגוגל משתמשים? נו, זה לא נחשב אם אתה לא יכול גם להפוך עץ בינארי"
הסכנה באנשי ה"לא נחשב" היא שחלילה יום אחד נתחיל להקשיב להם. אני לא יודע מה יעבוד בשבילכם, בחיים שלכם, בתעשיה שלכם, בסביבה שלכם - אתם תצטרכו לגלות את זה לבד. לא כדאי לעשות את החיים יותר קשים ממה שהם ולהוסיף הגבלות מלאכותיות. אם זה מתאים לכם ומקדם אתכם, זה נחשב.
1 419
# הדברים הקטנים
אין ספק שפיתרון הרבה מהבעיות בקוד דורש מחשבה, מיומנות, יצירתיות, היכרות עם עולם התוכן ושלל מיומנויות של מתכנתים חכמים. אבל הרבה פעמים דווקא הדברים הקטנים הם אלה שיכולים לשנות את החיים שלנו ביום יום.
הנה דוגמה לשינוי קוד פשוט מסוג זה. היה לי פרויקט knex בו לכל טבלה בבסיס הנתונים היה קובץ Repository שכלל קוד לפעולות בסיסיות עם הטבלה. לדוגמה עבור טבלה מדומיינת של משימות היה לנו קובץ שנראה בערך כך:
function create(db, task) {
return db('tasks').insert(task).then(([createdId]) => ({ ...task, id: createdId }));
}
function select(db, ...columns) {
return db('tasks').select(columns);
}
function destroy(db, task) {
return db('tasks').where('id', task.id).del();
}
function update(db, which, what) {
return db('tasks').where('id', which.id).update(what);
}
module.exports = { create, select, destroy, update };
אנשים אהבו את זה כי בתוכנית הראשית היית יכול לכתוב דברים כמו:
const newTask = await tasks.create(db, { text: 'new task', done: false });
await tasks.update(db, newTask, { done: true });
console.log(await tasks.select(db, '*'));
await tasks.destroy(db, newTask);
אבל הבעיה שכל מי שרצה להוסיף טבלה חדשה היה צריך לכתוב גם קובץ Repository, וכמעט כולם כללו את אותן פעולות בסיסיות, בנוסף לפעולות ספציפיות של אותה טבלה.
ה Refactoring במקרה הזה היה קסום כמו שהיה פשוט. יוצרים קובץ אחד בשם repo.js עם הקוד הבא:
module.exports = function(name) {
function create(db, item) {
return db(name).insert(item).then(([createdId]) => ({ ...item, id: createdId }));
}
function select(db, ...columns) {
return db(name).select(columns);
}
function destroy(db, item) {
return db(name).where('id', item.id).del();
}
function update(db, which, what) {
return db(name).where('id', which.id).update(what);
}
return { create, select, destroy, update };
}
ועכשיו מי שרוצה רק את הפעולות הבסיסיות מסתפק בשורה אחת:
module.exports = require('./repo')('tasks');
וגם מי שצריך יותר יכול לקחת את 4 הפעולות מ repo ולהוסיף עליהן פעולות חדשות.
וכן - הדוגמה שלי עברה ניקוי. בפרויקט אמיתי חיבור הקבצים לתשתית אחת דורש יותר עבודה ותמיד יהיה יותר מסובך מכתיבת קובץ אחד נוסף. ועדיין, מספיק בן אדם אחד שיארגן מחדש את התשתית כדי שכל המערכת תזוז יותר מהר.1 419
# תכנות מונחה דיבאגר
תכנות מונחה דיבאגר קורה כשיש לך ערימה של קוד שאתה לא מבין, ואתה צריך להוסיף לקוד הזה פיצ'ר או לתקן בו באג. במצב כזה אנחנו זורקים נקודות עצירה כמו שדייג זורק רשתות למים, ומקווים שאחת מנקודות העצירה תתפוס משהו שנראה כמו הבעיה. ברגע שתופסים את הבאג אנחנו הולכים למקום שנראה לנו לא נכון ומוסיפים שם טלאי, בתקווה לא לשבור קוד מסביב.
אחת הבעיות עם תכנות מונחה דיבאגר היא שזו שיטת עבודה שמשמידה את עצמה.
כשמתכנתים לא מבינים את המערכת עד הסוף, כל "תיקון" מכניס עוד באגים נסתרים או יוצר קשרים חדשים בין מקומות, ולאורך זמן ככל שהטלאים מצטברים הקוד נהפך להרבה יותר קשה להבנה ותחזוקה. פונקציה פשוטה של 4-5 שורות יכולה לקבל טלאי שיוסיף לה עוד 3-4 שורות, ואז כמה חודשים אחרי זה עוד טלאי כזה ושנתיים אחרי זה אנחנו בבור של 300 שורות עם אינסוף if-ים מקוננים שכל אחד מטפל במקרה קצה מאוד ספציפי.
בעיה שניה עם תכנות מונחה דיבאגר היא ששינוי גישה באמת לוקח המון זמן. מתכנתים שרוצים להתחיל לתקן קוד בגישה של Refactoring מתוך הבנה מעמיקה של הקוד ושל הבאג נדרשים קודם לקבל הבנה מעמיקה של הקוד, ובמערכות גדולות זה יכול לקחת חודשים ואפילו שנים. אנחנו גם צריכים תשתית בדיקות טובה כדי שנוכל לדעת אם ה Refactor שאנחנו מציעים שובר דברים במקומות אחרים במערכת. עוזר גם לשבור את המערכת לחלקים יותר קטנים בגישה של Micro Services, במיוחד כשמדובר על מערכות שכבר באוויר כמעט עשור ומספר שורות הקוד שלהן נספר במיליונים.
גם הדיבאגר הכי טוב בעולם לא יהפוך את הקוד שלכם לנסבל. אם תכנות מבוסס דיבאגר הוא הדרך היחידה לפתור בעיה, הגיע הזמן להתחיל לחשוב על פיתרונות אמיתיים לטווח הרחוק.
1 419
# פייק
שאלת תרגול פופולרית בקורסים מבקשת מהתלמידים להמיר מספר מכתיב עשרוני לספרות רומיות. אם אתם לא מכירים את שיטת הספירה הרומית קחו כמה רגעים להיכנס ללינק. אני אחכה.
התרגיל הוא פשוט ועוזר להתאמן על פעולות חילוק במספרים שלמים ושארית. אם נישאר רק עם המספרים הקטנים מ-40 אפשר לנסות את הפיתרון הבא:
symbols = (
[10, 'X'],
[5, 'V'],
[1, 'I' ]
)
def convert_to_roman(number):
result = ''
for i, r in symbols:
result += r * (number // i)
number = number % i
return result
קוד הבדיקה הבא משתמש בפיתרון ומראה שהוא עובד לפחות על 5 מספרים:
result = {
28: 'XXVIII',
12: 'XII',
6: 'VI',
32: 'XXXII',
21: 'XXI',
}
for k, v in result.items():
if convert_to_roman(k) != v:
raise Exception(f"Conversion failed for {k}. Expected: '{v}'; Got: '{convert_to_roman(k)}'")
print("All OK")
אבל כשננסה להפעיל את ההמרה על מספר כמו 19 נקבל שגיאה. ההמרה הנכונה של 19 לספירה רומית היא XIX אבל אצלנו הוא יופיע בתור XVIIII. איך מתקנים?
כיוון אחד יכול להיות לנסות לשכנע את המחשב לבדוק כל מיני אפשרויות לבנות את המספרים, ולכתוב את הקוד שמזהה ש 19 הוא 20-1 בדיוק כמו שהוא 15+4 אבל ש 20-1 יותר קצר לכתיבה ולכן כדאי להמיר לזה.
כיוון יותר קל הוא לרמות:
symbols = (
[10, 'X'],
[9, 'IX'],
[5, 'V'],
[4, 'IV'],
[1, 'I' ],
)
אם טבלת הסמלים שלי כוללת גם את 9 ו-4, אז כשהפונקציה תתקל ב 19 היא קודם כל תיקח את ה 10 ותהפוך אותו ל X, ואז את ה 9 שנשאר תהפוך ל IX. הפונקציה לא מספיק חכמה בשביל להבין ש 9 זה עשר פחות אחד, אבל גם לא צריכה להיות. את החוכמה אפשר להשאיר לרומאים.1 419
# הפונקציות map ו filter לאוביקטים ב JavaScript
הפונקציות Object.entries ו Object.fromEntries הן סוג של אוצר לכל מי שאוהב תכנות פונקציונאלי ו JavaScript כי הן מאפשרות לנו לסגור את הפער בין אוביקטים למערכים.
במערכים הפונקציות map ו filter מגיעות מובנות עם המערך, כך שאם יש לי רשימה של מספרים אני יכול בקלות לקבל את ריבועי המספרים, או את ריבועי המספרים הגדולים מ 50:
const numbers = [5, 6, 7, 8, 9, 10];
const squares = numbers.map(x => x * x);
const largeSquares = numbers.map(x => x * x).filter(x => x > 50);
אבל מה לגבי אוביקטים? הדרך הקלה להפעיל map ו filter על אוביקטים היא להפוך אותם למערכים, להפעיל את הלוגיקה ואז להחזיר חזרה לאוביקטים.
לדוגמה קחו אוביקט קונפיגורציה שמגדיר מספר URL-ים חשובים ליישום:
const urls = {
google: 'http://www.google.com',
ddg: 'http://www.duckduckgo.com',
bing: 'http://www.bing.com'
}
בעזרת Object.entries ו Object.fromEntries אני מריץ בקלות טרנספורמציות על האוביקט. למשל בשביל להפוך את כל ה URL-ים ל https ולוותר על כל מנוע חיפוש שהשם שלו ארוך מ-4 אותיות אני מריץ:
Object.fromEntries(
Object.entries(urls).
map(([key, value]) =>
[key, value.replace('http://', 'https://')]).
filter(([key, value]) => key.length < 5))
ומקבל חזרה:
{ ddg: 'https://www.duckduckgo.com', bing: 'https://www.bing.com' }
אנחנו יכולים לכתוב שרשראות ארוכות ככל שנרצה של map ו filter, כל עוד מקפידים ש map תמיד יחזיר זוגות של מפתח וערך.1 419
# זה היה שם לפניי
ואולי את יודעת יותר ממה שידע המתכנת שכתב את זה במקור?
ואולי את רואה יותר דברים ממה שהוא ראה?
ואולי המערכת נמצאת היום במצב אחר ממה שהיתה כשהקוד הזה נכתב במקור?
ואולי את יותר מנוסה ממה שהוא היה כשהוא כתב את זה במקור?
ואולי היום מקובל לעשות דברים אחרת?
ואולי כשהקוד יופיע במקום נוסף אפשר יהיה לכתוב אותו בצורה גנרית יותר?
כשאת משכפלת את הקוד שהיה שם קודם כדי לפתור בעיה חדשה את נותנת לו חותמת, חותמת של כאן ועכשיו, חותמת שאומרת "נכון לעכשיו זאת הגישה הכי טובה לעשות דברים", ו"אם הייתי מתחילה מחדש והקוד הישן לא היה שם, עדיין הייתי כותבת אותו בדיוק באותה צורה".
כשאת מעתיקה את הקוד הישן למקום חדש את נותנת ציון 100 לקוד הישן. את בטוחה שזה מגיע לו?
Вже доступно! Дослідження Telegram за 2025 — головні інсайти року 
