ค้นหาเว็บไซต์

typescript - การแมปวัตถุไปยังคุณสมบัติของมันเองเป็นสิ่งปฏิบัติที่ไม่ดีหรือไม่?


ฉันมักจะพบว่าตัวเองกำลังส่งต่อวัตถุที่มีรหัสหรือชื่อที่ระบุวัตถุนั้น แต่ก็เป็นสมบัติของวัตถุนั้นด้วย ตัวอย่างเช่น:

type ExampleItemName = string;
interface ExampleItem {
  name: ExampleItemName ;
  value: number;
}

ฉันมักจะต้องสร้างรายการของสิ่งเหล่านี้อยู่บ่อยครั้ง และฉันพบว่ามันสะดวกที่จะสามารถเข้าถึงสิ่งเหล่านี้ได้จาก name ของพวกมัน โครงสร้างข้อมูลที่สมเหตุสมผลที่สุดที่ฉันเห็นสำหรับสิ่งนี้คือ Map ตัวอย่างเช่น:

type ExampleItemList = Map<ExampleItemName, ExampleItem>;

นี่สะดวกและง่ายต่อการใช้งาน เพราะฉันสามารถเข้าถึงรายการใด ๆ ตาม name ด้วย Map.get แต่ฉันก็สามารถวนซ้ำทั้งรายการได้อย่างง่ายดายโดยใช้ for...of ด้วยเช่นกัน

อย่างไรก็ตาม สิ่งนี้รู้สึกไม่ถูกต้องสำหรับฉันเพราะข้อมูล name ถูกทำซ้ำ - มันมีทั้งในคีย์และในค่าของแมป หาก name ของ ExampleItem เปลี่ยนไป คีย์ที่สอดคล้องกันในแมปจะไม่เปลี่ยน ดังนั้นมันจึงรู้สึกเปราะบาง

นี่เป็นวิธีปฏิบัติที่ไม่ดีหรือไม่? ถ้าใช่ มีวิธีที่ดีกว่าในการทำให้ได้ฟังก์ชันที่ฉันต้องการหรือไม่?

คำตอบ #1

นี่ไม่ใช่การปฏิบัติที่ไม่ดีโดยตรง แต่มีเงื่อนไขบางประการที่ต้องปฏิบัติตาม มิฉะนั้นคุณอาจเจอปัญหาเหมือนปัญหาที่คุณกล่าวถึงเกี่ยวกับค่าที่คุณใช้เป็นคีย์แล้วเปลี่ยนแปลง รูปแบบนี้มักถูกเรียกว่าเป็นการทำคีย์ข้อมูลของคุณ การปฏิบัติที่ดีที่สุดคือใช้ค่าที่คุณรู้ว่าจะไม่เปลี่ยนแปลง เช่น ID เป็นคีย์ เนื่องจากพจนานุกรมมักช่วยลดค่าใช้จ่ายของฟังก์ชันการค้นหาที่มีราคาแพง จึงจำเป็นในหลายสถานการณ์ แต่ตามที่ฉันกล่าวไว้และคุณก็ชี้ให้เห็น คุณต้องเลือกใช้คีย์โดยใช้ค่าที่ไม่เปลี่ยนแปลงและไม่ซ้ำกัน หรือคุณจะต้องอัปเดตและจัดการค่าซ้ำทุกครั้งที่ใช้พจนานุกรม การเลือกวิธีแก้ปัญหาที่เหมาะสมนั้นขึ้นอยู่กับสถานการณ์ของคุณ

นอกจากนี้ ไลบรารีอย่าง 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>;