从多个嵌套数组中创建新的数据对象集合

3

我是一名帮助翻译的助手。

我有一个包含多个嵌套数组的复杂数据结构。

以下是当前的结构:

var contentData = {
  data: {
    content: [
      {
        type: "column",
        sections: [
          {
            sub: [
              {
                type: "heading-1",
                text: "Heading Text"
              }
            ]
          }
        ]
      },
      {
        type: "acc-item",
        sections: [
          {
            sub: [
              {
                type: "heading-1",
                text: "Heading Text"
              },
              {
                type: "ordered-item",
                text: "Item 1"
              },
              {
                type: "unordered-item",
                text: "Item 2"
              }
            ]
          }
        ]
      },
      {
        type: "acc-item",
        sections: [
          {
            sub: [
              {
                type: "heading-1",
                text: "Heading Text 2"
              }
            ]
          }
        ]
      }
    ]
  }
}

我希望的是:

  1. 我想把所有的ordered-item & unordered-item分组到一个新对象中,如{type: 'list', items:[all list items]}

  2. 我需要提取所有在sub中的项目,并将其推送到新的对象embedded中,并且它应该放置在根级别下面,如下所示:

    {type:"acc-item",embedded:[{type:"heading-1",text:"Heading Text 2"}]};

目前我已经完成了以下工作:

我可以把acc-item分组,但无法分组ordered-item & unordered-item

因此,我的最终期望结果应该像这样:

[{
  "type": "column",
  "embedded": [
    {
      "type": "heading-1",
      "text": "Heading Text"
    }
  ]
},
{
  "type": "acc-group",
  "items": [
    {
      "type": "acc-item",
      "embedded": [
        {
          "type": "heading-1",
          "text": "Heading Text"
        },
        {
          "type": "list",
          "items": [
            {
              "type": "ordered-item",
              "text": "Item 1"
            },
            {
              "type": "unordered-item",
              "text": "Item 2" 
            }
          ]
        }
      ]
    },
    {
      "type": "acc-item",
      "embedded": [
        {
          "type": "heading-1",
          "text": "Heading Text 2"
        }
      ]
    }
  ]
}]

以下是我的代码:

var group,contentData={data:{content:[{type:"column",sections:[{sub:[{type:"heading-1",text:"Heading Text"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"Heading Text"},{type:"ordered-item",text:"Item 1"},{type:"unordered-item",text:"Item 2"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"Heading Text 2"}]}]}]}},types=[["list",["ordered-item","unordered-item"]],["accordion",["acc-item"]]];

var result = contentData.data.content.reduce((r, o) => {
  var type = (types.find(({ 1: values }) => values.indexOf(o.type) > -1)|| {})[0];
  if (!type) {
    r.push(o);
    group = undefined;
    return r;
  }
  if (!group || group.type !== type) {
    group = { type, items: [] };
    r.push(group);
  }
  group.items.push(o);
  return r;
}, []);

document.body.innerHTML = '<pre>' + JSON.stringify(result, null, '  ') + '</pre>';

2个回答

1
您可以将最后的项数组以及最后的嵌入式数组存储起来,并在找到列类型之前一直使用它们。

var contentData = { data: { content: [{ type: "column", sections: [{ sub: [{ type: "heading-1", text: "Heading Text" }] }] }, { type: "acc-item", sections: [{ sub: [{ type: "heading-1", text: "Heading Text" }, { type: "ordered-item", text: "Item 1" }, { type: "unordered-item", text: "Item 2" }] }] }, { type: "acc-item", sections: [{ sub: [{ type: "heading-1", text: "Heading Text 2" }] }] }] } },
    list = ["ordered-item", "unordered-item"],
    lastItems, lastEmbedded,
    result = contentData.data.content.reduce((r, { type, sections }) => {
        if (type === 'column') {
            r.push({ type, embedded: sections.reduce((q, { sub }) => q.concat(sub), []) });
            lastItems = undefined;
            lastEmbedded = undefined;
            return r;
        }
        if (!lastItems) r.push({ type: "acc-group", items: lastItems = [] });
        lastItems.push(...sections.map(({ sub }) => ({
            type,
            embedded: sub.reduce((q, o) => {
                if (list.includes(o.type)) {
                    if (!lastEmbedded) q.push({ type: 'list', items: lastEmbedded = [] });
                    lastEmbedded.push(o);
                } else {
                    q.push(o);
                    lastEmbedded = undefined;
                }
                return q;
            }, [])
        })));
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }


一个问题是,"ordered-item", "unordered-item" 在列内没有分组。 - user007
你能使用这个 var contentData={data:{content:[{type:"column",sections:[{sub:[{type:"heading-1",text:"标题文本"},{type:"ordered-item",text:"项目 1"},{type:"unordered-item",text:"项目 2"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"标题文本"},{type:"ordered-item",text:"项目 1"},{type:"unordered-item",text:"项目 2"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"标题文本 2"}]}]},{type:"column",sections:[{sub:[{type:"heading-1",text:"标题文本"},{type:"ordered-item",text:"项目 1"},{type:"unordered-item",text:"项目 2"}]}]}]}}; 吗? - user007
这个结果应该是什么样子? - Nina Scholz
列表未在列内分组,因此预期结果为[{type:"column",embedded:[{type:"heading-1",text:"标题文本"},{type:"list",items:[{type:"ordered-item",text:"项目1"},{type:"unordered-item",text:"项目2"}]}]}]; - user007

0

Array.prototypeObject.prototype方法非常适合这种情况。

你说得对,这是一种复杂的逻辑。

我建议你一定要为此编写一些单元测试,并尝试将其分解成几个部分。

以下是我考虑如何处理它的方式:

1. 按类型分组以创建您的组。

实际上,我正在创建一个比您在此处请求的更通用的解决方案。也就是说,我不仅仅是将“acc-item”分组,而是所有内容都进行分组。

我快速搜索了“array group by javascript”,找到了this answer,其中建议使用Array.reduce,所以我们就这样做吧。

    const groupedData = contentData.data.content.reduce((acc, cur) => {
        //Check if this indexed array already exists, if not create it. 
        const currentArray = (acc[`${cur.type}-group`] && acc[`${cur.type}-group`].items) || [];

        return {
          ...acc,
          [`${cur.type}-group`]: {
              type: `${cur.type}-group`, 
              items: [...currentArray, cur]
          }
        }
    }, {}); 

2. 现在我们需要查看每个项目的子项,并仅对列表项进行分组。

为此,我们基本上想要找到所有的 `item -> sections -> sub -> types` 并将它们过滤成两个数组。关于如何使用过滤器创建两个数组的快速谷歌搜索给了我 this answer

不过首先,我们需要展开那些 sections-> subs 的东西,所以让我们来做这件事。

function flattenSectionsAndSubs(item) {
    return {
        type: item.type, 
        subs: item.sections.reduce((acc, cur) => ([...acc, ...cur.sub]), [])
    }; 
}

然后我会将那个分区函数复制粘贴进去:

function partition(array, isValid) {
  return array.reduce(([pass, fail], elem) => {
    return isValid(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]];
  }, [[], []]);
}


    const listTypes = ['ordered-item', 'unordered-item']; 
    function createEmbeddedFromItem(item) {
        const [lists, nonLists] = partition(item.subs, (v) => listTypes.includes(v.type); 

      return {
         type: item.type, 
         embedded: [
             ...nonLists, 
             {
                type: "list", 
                items: lists
             }
         ]
      }
    }

将所有这些放在一起,我们得到。

const contentData = {
  data: {
    content: [{
        type: "column",
        sections: [{
          sub: [{
            type: "heading-1",
            text: "Heading Text"
          }]
        }]
      },
      {
        type: "acc-item",
        sections: [{
          sub: [{
              type: "heading-1",
              text: "Heading Text"
            },
            {
              type: "ordered-item",
              text: "Item 1"
            },
            {
              type: "unordered-item",
              text: "Item 2"
            }
          ]
        }]
      },
      {
        type: "acc-item",
        sections: [{
          sub: [{
            type: "heading-1",
            text: "Heading Text 2"
          }]
        }]
      }
    ]
  }
}


function partition(array, isValid) {
  return array.reduce(([pass, fail], elem) => {
    return isValid(elem) ? [
      [...pass, elem], fail
    ] : [pass, [...fail, elem]];
  }, [
    [],
    []
  ]);
}


function flattenSectionsAndSubs(item) {
  return {
    type: item.type,
    subs: item.sections.reduce((acc, cur) => ([...acc, ...cur.sub]), [])
  };
}

const listTypes = ['ordered-item', 'unordered-item'];

function createEmbeddedFromItem(item) {
  const [lists, nonLists] = partition(item.subs, (v) => listTypes.includes(v.type));

    return {
      type: item.type,
      embedded: [
        ...nonLists,
        {
          type: "list",
          items: lists
        }
      ]
    }
  }


  const groupedData = contentData.data.content.reduce((acc, cur) => {
    //Check if this indexed array already exists, if not create it. 
    const currentArray = (acc[`${cur.type}-group`] && acc[`${cur.type}-group`].items) || [];

    const flattenedItem = flattenSectionsAndSubs(cur);
    const embeddedItem = createEmbeddedFromItem(flattenedItem);
    return {
      ...acc,
      [`${cur.type}-group`]: {
          type: `${cur.type}-group`, 
          items: [...currentArray, embeddedItem]
      }
    }
  }, {});

  console.log(groupedData);

现在这并不完全符合您的要求 - 但它应该可以工作。

如果数组不为空,您可以添加自己的内容以仅添加列表项,并停止列处于自己的组中。

问题是 - 说实话,似乎您创建了一个没有匹配结构的项目数组,这就是为什么我这样做的原因。


flattenSectionsAndSubs 中,.flat 在IE中无法工作。 - user007
请参考此答案:https://dev59.com/BZnga4cB1Zd3GeqPYnCF您可能需要使用polyfill,或使用babel编译您的生产代码,或稍微更改语法。 - dwjohnston
我已经将代码复制到这里,以便使用babel编译 - 请查看它在此处是否有效:https://codepen.io/dwjohnston/pen/KLBwza?editors=1111 - dwjohnston
我可以看到一个空的“列表”被添加到结果中。 - user007
@user007 是的。请看我的最终评论。你可以自己编辑代码,让它完全符合你的要求。我已经向你展示了一般的流程。 - dwjohnston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接