Home > Software design >  Optimized solution for merging list items
Optimized solution for merging list items

Time:11-10

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

  • Related