javascript - 如何将Javascript对象解析为对象数组

标签 javascript typescript

我有一个数据对象,需要一个简单的 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.' },
]

最佳答案

由于这是 TypeScript,因此定义输入和输出类型的接口(interface)很有帮助。根据您的示例,我将猜测以下内容,但您显然可以更改它们以适合您的用例:

更新:显然 Data 中的 links 属性应该是可选的,因此我将更改它以反射(reflect)这一点。

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+\$)/ 的匹配项分隔的 block 。该表达式指定一段文本,该文本以美元符号 $ 开头,后跟“单词”字符(字母、数字和下划线),然后以另一个美元符号结束。同样,如果这与您的用例不完全匹配,您可以调整正则表达式来实现这一点。

现在我们有一个要处理的字符串数组。本质上,接下来的两行将每个字符串映射到一个对象。如果字符串是数据的 links 属性的键,它将查找该属性(d.links?.[s] 使用 s 作为 d.links 对象中的属性键;如果 d.links?.[s]undefined,则表示 s 不作为键出现。请注意,?. 表示法是 optional chaining,并考虑了 d.links 本身可能丢失的可能性)并将其格式化为 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 link to code

<小时/>

更新:你问如何走另一个方向。这确实是一个单独的问题,但这是我的答案:

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$"

现在,可能存在各种边缘情况,您应该考虑它们。但我几乎可以肯定已经完成了。祝你好运!

Link to updated code

关于javascript - 如何将Javascript对象解析为对象数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60491367/

相关文章:

javascript - Typescript Vue 中的 Vuex 状态对象检测

angular - TypeScript 返回类型 : void

angular - Angular 中支持对象不同错误

angular - 如何导入 moment.js angular2

javascript - xorShift128+ 的最大值(或者我的实现有什么问题)

javascript - RoR 使用 javascript 渲染部分内容

php - 页面无法打印正常吗?

typescript - 网页包 ts-loader : change tsconfig filename

javascript - Protractor : mocking EventSource

javascript - 如何在nodejs中将嵌套的JSON转换为excel