'how to transform strin array in to array of objects with multiple elements

I have this array:

data: [
  ‘big text 1 here.’,
  ‘big text 2 here followed by list:’,
  '-this is list item;',
  '-this is another list item;',
  ‘big text 3 here followed by list:’,
  '-this is list item;',
  '-this is another list item;',
  '-this is another list item;',
  ‘big text 4 here.’
],

I want to turn it to something like:

data: [
  { 
   text: ‘big text 1 here.’
   list: []
  },
  { 
   text: ‘big text 2 here followed by list:’
   list: [
     '-this is list item;',
     '-this is another list item;'
   ]
  },
  { 
   text: ‘big text 3 here followed by list:’
   list: [
     '-this is list item;',
     '-this is another list item;',
     '-this is another list item;',
   ]
  },
  { 
   text: ‘big text 4 here.’
   list: []
  },
],

I am trying to achieve this this way

interface item {
  text: string;
  list: string[];
};

const newArr: item[] = [];

this.item.content.forEach((x, i) => {
  if(x.startsWith('-') && x.endsWith(';')) {
    //this is bullet list, need to add that to the previous item...
  } else {
    newArr.push({ text: x, list: []});
  }
});

I am not sure if I am doing it in a right way, because there is no way I can think of how to access previous element in newArr; Any ideas how to do it proper way?

That is the output that I want to have based on that data:

<p>big text 1 here</p>
<p>big text 2 here followed by list:</p>
<ul>
  <li>this is list item</li>
  <li>this is another list item</li>
</ul>
<p>big text 3 here followed by list</p>
<ul>
  <li>this is list item</li>
  <li>this is another list item</li>
  <li>this is another list item</li>
<p>big text 4 here</p>


Solution 1:[1]

I'd use Array.reduce for this

interface DataItem {
    text: string
    list: string[]
}

function isListItem(item: string): boolean {
    return item.startsWith('-') && item.endsWith(';')
}

const transformedData = data.reduce<DataItem[]>((acc, item) => {
    // if item is list item
    if (isListItem(item)) {
        // get last DataItem from acc
        const lastDataItem = acc[acc.length - 1]

        // and use it as list item's parent
        if (lastDataItem) {
            lastDataItem.list.push(item)
        } else {
            console.error(`Parent text not found for list item ${item}`)
        }

        return acc
    }

    // if item is not list item, use it as new parent/DataItem
    const dataItem: DataItem = {
        text: item,
        list: []
    }

    return [...acc, dataItem]
}, [])

Playground link

Solution 2:[2]

I would go for ngTemplateOutlet option for generating li based on your list array.

<ul *ngFor="let item of data">
   <ng-container
       *ngTemplateOutlet="
          !(item.startsWith('-') && item.endsWith(';')) ? ulTemplate : bulletItem;
         context: { $implicit: item }
     "
     >
   </ng-container>
</ul>

<ng-template #ulTemplate let-item>
   <li>{{ item }}</li>
</ng-template>

<ng-template #bulletItem let-item>
  <ul>
    <li>{{ item }}</li>
  </ul >
</ng-template>

Now, this way you have redere the right ng-template based on your start character. Complete demo in this link Stackblitz Link

Solution 3:[3]

You could try the following below snippet

let data = [
  'big text 1 here.',
  'big text 2 here followed by list:',
  '-this is list item;',
  '-this is another list item;',
  'big text 3 here followed by list:',
  '-this is list item;',
  '-this is another list item;',
  '-this is another list item;',
  'big text 4 here.'
];

function format_array_to_object(data) {
    let new_data = [];
  let previous_data = {};
  data.forEach( function( item, key ){
    if ( item.trim().charAt(0) === '-' ) {
       previous_data.list.push( item );
    } else {
      if ( Object.keys(previous_data).length !== 0 ) {
        new_data.push(previous_data);
        previous_data = {};
        let current_elem = { text: item, list:[] };
        previous_data = current_elem;
      } else {
        let current_elem = { text: item, list:[] };
        previous_data = current_elem;
      }

      if ( ( key + 1 ) === data.length ) {
        new_data.push(previous_data);
      }

    }
  } );
  return new_data;
}

console.log(format_array_to_object(data));

Solution 4:[4]

Just saying your strings don't have the correct quotations marks, and ' are not the same and won't work for a string.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 GRD
Solution 3 Mahedi Hasan
Solution 4 Mark Rotteveel