Методы Array.prototype и Object.prototype идеально подходят для такого рода вещей.
И вы правы, что это какая-то сложная логика.
Я бы предложил, чтобы вам определенно потребовались какие-то юнит-тесты для этого, и попробуйте разбить на части.
Вот как я думаю, я бы сделал это.
1. Группа По типу для создания ваших групп. .
На самом деле я создаю более общее решение, о котором вы просили здесь. То есть, я не просто группирую 'acc-item', но всё.
Я быстро выполнил поиск 'group array by javascript', и он дал нам этот ответ , который предлагает использовать 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 -> section -> sub -> и отфильтровать их в два массива. Быстрый гугл о том, как создать два массива с использованием фильтра, дает мне этот ответ.
Во-первых, нам нужно сгладить эту секцию -> подпрограмму, так что давайте просто сделаем это.
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);
Теперь это не совсем соответствует тому, что вы просили, но, вероятно, должно сработать.
Вы можете добавить свои собственные биты только в элемент списка, если массив не пустой, и чтобы столбец не входил в свою собственную группу.
Дело в том, что, кажется, немного красного флажка, что вы создадите массив элементов без соответствующих структур, поэтому я сделал это таким образом.