I have an array of content blocks like so:
interface Content{
type: string,
content: string | string[]
}
const content: Content[] = [
{
type: "heading"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
{
type: "list_item"
content: "whatever"
},
{
type: "list_item"
content: "whatever"
},
{
type: "list_item"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
{
type: "heading"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
{
type: "list_item"
content: "whatever"
},
{
type: "list_item"
content: "whatever"
},
{
type: "list_item"
content: "whatever"
},
{
type: "list_item"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
]
I'm trying to write a function which can merge these list_item
's into a list
with an array for the content of each. So, the output of the function for the above input should be:
[
{
type: "heading"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
{
type: "list"
content: ["whatever","whatever","whatever"]
},
{
type: "para"
content: "whatever"
},
{
type: "heading"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
{
type: "para"
content: "whatever"
},
{
type: "list"
content: ["whatever", "whatever", "whatever", "whatever"]
},
{
type: "para"
content: "whatever"
},
]
I've been trying to use a three-pointer system, looping over the array from i=1 to i < length - 1, tracking the prev
, curr
and next
blocks, however, I am getting very stuck with the logic and how I should handle the cases.
I feel like this is quite a simple problem for more experienced algorithm designers, so, I was looking for some guidance.
CodePudding user response:
A fairly straightforward loop, checking if the current.type
is a 'list_item' and either instantiating a new object with type: 'list'
or pushing to the previous object if we've already created one.
const content = [{ type: "heading", content: "whatever" }, { type: "para", content: "whatever" }, { type: "para", content: "whatever" }, { type: "list_item", content: "list1 1" }, { type: "list_item", content: "list1 2" }, { type: "list_item", content: "list1 3" }, { type: "para", content: "whatever" }, { type: "heading", content: "whatever" }, { type: "para", content: "whatever" }, { type: "para", content: "whatever" }, { type: "list_item", content: "list2 1" }, { type: "list_item", content: "list2 2" }, { type: "list_item", content: "list2 3" }, { type: "list_item", content: "list2 4" }, { type: "para", content: "whatever" },];
const res = [];
for (const item of content) {
// If current item.type is 'list_item'...
if (item.type === 'list_item') {
// check if last element in result is of type 'list'...
if (res[res.length - 1].type !== 'list') {
// if not push one.
res.push({ type: 'list', content: [] });
}
// Push the item.content into the 'content' array of the last element of the result.
res[res.length - 1].content.push(item.content);
} else {
// Otherwise push a copy of the current item into the result
res.push({ ...item });
}
}
console.log(res);
An alternative that avoids having to repeatedly access the tail of the array is to store a reference to the active list array in a temp variable and set it to null every time a sequence of list_item
s ends.
const content = [{ type: "heading", content: "whatever" }, { type: "para", content: "whatever" }, { type: "para", content: "whatever" }, { type: "list_item", content: "list1 1" }, { type: "list_item", content: "list1 2" }, { type: "list_item", content: "list1 3" }, { type: "para", content: "whatever" }, { type: "heading", content: "whatever" }, { type: "para", content: "whatever" }, { type: "para", content: "whatever" }, { type: "list_item", content: "list2 1" }, { type: "list_item", content: "list2 2" }, { type: "list_item", content: "list2 3" }, { type: "list_item", content: "list2 4" }, { type: "para", content: "whatever" },];
const res = [];
let list = null;
for (const item of content) {
if (item.type === 'list_item') {
if (!list) {
list = { type: 'list', content: [] };
res.push(list);
}
list.content.push(item.content);
} else {
if (list) {
list = null;
}
res.push({ ...item });
}
}
console.log(res);
CodePudding user response:
I think just use js builtin array method is optimized enough
const a = [
{
type: "heading",
content: "whatever"
},
{
type: "para",
content: "whatever"
},
{
type: "para",
content: "whatever"
},
{
type: "list_item",
content: "whatever 1"
},
{
type: "list_item",
content: "whatever 2"
},
{
type: "list_item",
content: "whatever 3"
},
{
type: "para",
content: "whatever"
},
{
type: "heading",
content: "whatever"
},
{
type: "para",
content: "whatever"
},
{
type: "para",
content: "whatever"
},
{
type: "list_item",
content: "whatever 4"
},
{
type: "list_item",
content: "whatever 5"
},
{
type: "list_item",
content: "whatever 6"
},
{
type: "list_item",
content: "whatever 7"
},
{
type: "para",
content: "whatever"
},
]
let listItemContent = [];
const res = a.reduce((total, v) => {
if(v.type === "list_item") {
listItemContent.push(v.content);
}
else if(listItemContent.length > 0) {
total.push({type: "list", content: listItemContent});
listItemContent = [];
} else {
total.push(v);
}
return total;
}, []);
if(listItemContent.length > 0) {
res.push({type: "list", content: listItemContent});
}
console.log(res)