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));