TypeScript - האם זו פרקטיקה רעה למפות אובייקט לנכס שלו עצמו?
לעיתים קרובות אני מוצא את עצמי מעביר חפצים שיש להם מזהים או שמות שמזהים אותם אך הם גם נכס של החפץ. לדוגמה:
type ExampleItemName = string;
interface ExampleItem {
name: ExampleItemName ;
value: number;
}
אני גם לרוב צריך ליצור רשימות של הפריטים הללו, ואני מוצא שזה נוח להיות מסוגל לגשת לפריטים לפי שם שלהם. מבנה הנתונים ההגיוני ביותר שאני רואה עבור זה הוא Map, כלומר:
type ExampleItemList = Map<ExampleItemName, ExampleItem>;
זה נוח ונגיש, שכן אני יכול לגשת לכל פריט לפי ה-name שלו עם Map.get אבל אני גם יכול בקלות לעבור על כל הרשימה בעזרת for...of.
עם זאת, זה מרגיש לי לא נכון כי המידע של name משוכפל - הוא קיים גם במפתח וגם בערך של המפה. אם ה-name של ExampleItem משתנה, המפתח התואם במפה לא משתנה, ולכן זה מרגיש פגיע.
האם זו פרקטיקה רעה? אם כן, מהי דרך טובה יותר להשיג את הפונקציונליות שאני מחפש?
תשובה #1
זו לא פרקטיקה רעה בפני עצמה, אבל יש כמה תנאים שצריך לעמוד בהם אחרת תיתקל בבעיות כמו הבעיה שהזכרת עם הערך שאתה ממפה לפי שינויו. תבנית זו בדרך כלל מכונה פעולת המיפוי של הנתונים שלך. עדיף למפות את הנתונים שלך לפי ערכים שאתה יודע שלא ישתנו, כמו מזהה. מכיוון שמילונים לעיתים קרובות יכולים להפחית את העלות של פונקציות חיפוש יקרות, הם נחוצים במצבים רבים, אבל כמו שציינתי וכמו שהדגשת, עליך למפות אותם לפי ערך שלא ישתנה ואשר ייחודי או שתצטרך לעדכן ולנהל כפילויות כל פעם שאתה משתמש במילון. איזו פתרון נדרש תלוי במצב שלך.
בנוסף, ספריות כמו lodash כוללות פונקציות מובנות (שאתה יכול בקלות לכתוב בעצמך) שלוקחות נתונים וממפות אותם עבורך. כמו כן, תראה סוגים אלו של רעיונות בשימוש נרחב בשפות אחרות גם אם הם נקראים בשמות שונים (Python: dictionary, Java: hashMap, וכו׳).
תשובה #2
אם זה לא מאוד לא נוח, אתה יכול לנסות ליצור את מבנה הרשימה שלך שמספק את הצרכים שלך, כמו המבנה הבא.
class NameMap<K, V extends { name: K }> {
private items: Set<V> = new Set();
Add(value: V) { this.items.add(value); }
Get(key: K) {
for(let e of this.items.values()) if(e.name == key) return e;
return undefined;
}
[Symbol.iterator]() { return this.items[Symbol.iterator](); };
}
type ExampleItemList = NameMap<ExampleItemName, ExampleItem>;
