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

javascript - วิธีการแปลงวัตถุของ Javasript เป็นอาเรย์ของวัตถุ


ฉันมีวัตถุของข้อมูลและต้องการวิธีการ js ง่ายๆ เพื่อแปลงวัตถุนี้ไปยังรูปแบบที่ตัวแก้ไขข้อความ quill รองรับ (แทนที่ตัวแทนด้วยลิงก์ของพวกมัน)

สิ่งที่ฉันมี:

const data = {
      text: 'Hello this is $link1$ and here is $link2$ and now finally $link3$ cool.',
      links: {
        $link3$: ['link 3', 'https://www.link3.com'],
        $link1$: ['link 1', 'https://www.link1.com'],
        $link2$: ['link 2', 'https://www.link2.com']
      }
};

ต้องการฟังก์ชัน javascript/typescript แบบง่าย ซึ่งฉันส่งอ็อบเจ็กต์ของ data เข้าไปและส่งค่ากลับดังนี้:

สิ่งที่ฉันต้องการ:

[
 { insert: 'Hello this is ' },
 {
   insert: 'link 1',
   attributes: { link: 'https://www.link1.com' }
 },
 { insert: ' and here is' },
 {
   insert: 'link 2',
   attributes: { link: 'https://www.link2.com' }
 },
 { insert: ' and now finally' },
 {
   insert: 'link 3',
   attributes: { link: 'https://www.link3.com' }
 },
 { insert: ' cool.' },
]

คำตอบ #1

เนื่องจากนี่คือ TypeScript จึงเป็นประโยชน์ที่จะกำหนด interfaces สำหรับประเภทของ input และ output จากตัวอย่างของคุณ ผมจะเดาดังต่อไปนี้ แต่คุณสามารถเปลี่ยนแปลงได้ตามกรณีการใช้งานของคุณ:

อัปเดต: ดูเหมือนว่า links ใน Data ควรจะเป็นอ็อปชัน ดังนั้นฉันจะเปลี่ยนมันให้สะท้อนสิ่งนั้น

interface Data {
    text: string;
    links?: { [k: string]: [string, string] | undefined }
}

interface ParsedData {
    insert: string;
    attributes?: { link: string }
}

และตอนนี้เราต้องการกำหนดฟังก์ชัน parse() ที่รับอาร์กิวเมนต์ประเภท Data และสร้างผลลัพธ์ประเภท ParsedData[] นี่คือหนึ่งในวิธีที่เป็นไปได้ในการทำมัน:

const parse = (d: Data): ParsedData[] => d.text.
    split(/(\$\w+\$)/).
    map(s => ({ s: s, l: d.links?.[s] })).
    map(({ s, l }) => l ? { insert: l[0], attributes: { link: l[1] } } : { insert: s });

สิ่งที่ฉันทำที่นี่คือการแบ่งสตริง text ออกเป็นชิ้น ๆ ซึ่งถูกแยกด้วยผลลัพธ์ที่ตรงกับนิพจน์ปกติ /(\$\w+\$)/ นิพจน์นั้นระบุชิ้นส่วนของข้อความที่เริ่มต้นด้วยสัญลักษณ์ดอลลาร์ $ ตามด้วยตัวอักษร "word" (ตัวอักษร ตัวเลข และตัวขีดล่าง) แล้วจบด้วยสัญลักษณ์ดอลลาร์อีกตัวอีกครั้ง อีกครั้ง หากสิ่งนี้ไม่ตรงกับกรณีการใช้งานของคุณ คุณอาจปรับแต่งนิพจน์ปกติเพื่อให้ตรงได้

ตอนนี้เรามีอาร์เรย์ของสตริงที่จะถูกประมวลผล โดยพื้นฐานแล้วสองบรรทัดถัดไปจะทำการแมปแต่ละสตริงไปยังวัตถุ หากสตริงนั้นเป็นคีย์ของคุณสมบัติ links ของข้อมูล มันจะทำการค้นหาคุณสมบัติ (d.links?.[s] กำลังใช้ s เป็นคีย์ของคุณสมบัติในวัตถุ d.links; ถ้า d.links?.[s] เป็น undefined หมายความว่า s ไม่มีอยู่เป็นคีย์) และจัดรูปแบบเป็นวัตถุ insert/attributes มิฉะนั้นมันจะใช้สตริงนั้นเป็นคุณสมบัติ insert โดยไม่มี attributes

นี่คือผลลัพธ์สำหรับตัวอย่างโค้ดของคุณ:

console.log(JSON.stringify(parse(data)));
/* [
    {"insert":"Hello this is "},
    {"insert":"link 1","attributes":{"link":"https://www.link1.com"}},
    {"insert":" and here is "},
    {"insert":"link 2","attributes":{"link":"https://www.link2.com"}},
    {"insert":" and now finally "},
    {"insert":"link 3","attributes":{"link":"https://www.link3.com"}},
    {"insert":" cool."}
    ] */

นี่ดูเหมือนกับสิ่งที่คุณต้องการ แน่นอนว่ามีกรณีขอบเขตที่อาจเกิดขึ้นได้หลายประเภท ดังนั้นคุณควรระมัดระวังอย่างมากในการระบุให้ชัดเจนว่าคุณต้องการให้เกิดอะไรขึ้นเมื่อคุณได้รับข้อมูลนำเข้าที่ “แปลก” ตัวอย่างเช่น ฟังก์ชั่นทำสิ่งนี้:

console.log(JSON.stringify(parse({
    text: "What do you $expect$ to happen here",
    links: {
        " to happen here": ["oopsie", "doopsie"],
    }
})))
/* [
    {"insert":"What do you "},
    {"insert":"$expect$"},
    {"insert":"oopsie","attributes":{"link":"doopsie"}}
    ] */

นั่น 'ถูกต้อง' ไหม? ใครจะรู้ละ ดังนั้นจงระมัดระวัง

โอเค หวังว่านี่จะช่วยได้ โชคดีนะ!

ลิงก์ Playground ไปยังโค้ด

อัปเดต: คุณถามว่าจะไปทางตรงข้ามได้อย่างไร นั่นเป็นคำถามที่แยกออกไปจริง ๆ แต่ต่อไปนี้คือคำตอบของฉัน:

function unparse(parsedData: ParsedData[]): Data {

    function uniqueLinkKey(links: { [k: string]: any }, displayString: string): string {
        const k = displayString.replace(/\W/g, "");
        let i: "" | number;
        for (i = ""; "$" + k + i + "$" in links; i = +i + 1) { }
        return "$" + k + String(i) + "$";
    }

    const ret: Required<Data> = { text: "", links: {} };
    for (let d of parsedData) {
        if (d.attributes) {
            const key = uniqueLinkKey(ret.links, d.insert);
            ret.text += key;
            ret.links[key] = [d.insert, d.attributes.link];
        } else {
            ret.text += d.insert;
        }
    }
    return ret;

}

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้เมื่อป้อนผลลัพธ์ของฟังก์ชันก่อนหน้าเข้าไป:

console.log(JSON.stringify(unparse(parsedData)));
/* { "text":"Hello this is $link1$ and here is $link2$ and now finally $link3$ cool.",
      "links":{
          "$link1$":["link 1","https://www.link1.com"],
          "$link2$":["link 2","https://www.link2.com"],
          "$link3$":["link 3","https://www.link3.com"]
}} */

การดำเนินการข้างต้นมีลักษณะเป็นสไตล์แบบคำสั่งมากกว่า เพราะการใช้แนวทางแบบฟังก์ชันล้วนอาจหนาแน่นเกินไป สิ่งที่ "น่าสนใจ" เพียงอย่างเดียวที่เกิดขึ้นคือคุณอาจไม่ต้องการเขียนทับคีย์ลิงก์หากพวกมันมีข้อความแสดงผลเดียวกัน ดังนั้นฟังก์ชัน uniqueLinkKey() จะทำให้แน่ใจว่ามันสร้างคีย์ลิงก์ใหม่ที่ยังไม่ปรากฏอยู่ในออบเจ็กต์ links ที่กำลังสร้าง:

console.log(JSON.stringify(unparse([
    { insert: "Repeats are bad: " },
    { insert: "ECHO", attributes: { link: "https://one.example.com" } },
    { insert: " and " },
    { insert: "ECHO", attributes: { link: "https://two.example.com" } },
    { insert: " and " },
    { insert: "ECHO", attributes: { link: "https://three.example.com" } },
    { insert: "!" },
])));
/* {"text":"Repeats are bad: $ECHO$ and $ECHO1$ and $ECHO2$!",
    "links":{
        "$ECHO$":["ECHO","https://one.example.com"],
         "$ECHO1$":["ECHO","https://two.example.com"],
         "$ECHO2$":["ECHO","https://three.example.com"]
}} */

ลิงก์ทั้งสามมีข้อความแสดงผล "ECHO" แต่ฟังก์ชันจะสร้างคีย์ที่แตกต่างกันสามค่า: "$ECHO$", "$ECHO1$", และ "$ECHO2$".

ตอนนี้อีกครั้ง อาจมีกรณีขอบเขตหลากหลายประเภทและคุณควรพิจารณาพวกมัน แต่ฉันเกือบจบที่นี่แล้ว โชคดี!

ลิงก์ไปยังโค้ดที่อัปเดต

คำตอบ #2

นี่คือฟังก์ชันที่ฉันเขียนอย่างรวดเร็ว มันสมมติว่าลิงก์ปรากฏในสตริงตามลำดับตัวอักษร หากไม่เป็นเช่นนั้นคุณจะต้องเรียงลำดับตามตัวที่ปรากฏในสตริงก่อน

const data = {
      text: 'Hello this is $link1$ and here is $link2$ and now finally $link3$ cool.',
      links: {
        $link3$: ['link 3', 'https://www.link3.com'],
        $link1$: ['link 1', 'https://www.link1.com'],
        $link2$: ['link 2', 'https://www.link2.com']
      }
};

const format = (data) => {
  let str = data.text;
  const result = [];
  Object.keys(data.links).sort().map((key) => {
    if (str.indexOf(key) > 0) {
      result.push({ insert: str.substring(0, str.indexOf(key)) })
    }
    result.push({
      insert: data.links[key][0],
      attributes: { link: data.links[key][1] }
    });
    str = str.substring(str.indexOf(key) + key.length)
  })
  if (str.length) {
    result.push({
      insert: str
    })
  }
  return result
}

console.log(format(data));