Home > Enterprise >  how to alter reduce function so it produce different result
how to alter reduce function so it produce different result

Time:05-06

I have this array:

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

I want to transform that array into

"data": [
   {
    section: "section1",
    content: [
    {
      "text": "big text 1 here.",
      "list": []
    },
    }
    {
      "text": "big text 2 here followed by list:",
      "list": [
        "-this is list item;",
        "-this is another list item;"
      ]
    },
    ]
   }
   {
    section: "section2"
    content:[
    {
      "text": "big text 3 here followed by list:",
      "list": [
        "-this is list item;",
        "-this is another list item;",
        "-this is another list item;"
      ]
    },
    ]
   }
   {
   section: "section3",
   content: [
    {
      "text": "big text 4 here.",
      "list": []
    }
    ]
   }
  ]

I have this code that doing about that:

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]
}, [])

What it missing is ignoring the sections. Any ideas how to modify that reduce function so it will produce expected result?

To determine a section I came up with this funciton:

function isSection(item: string): boolean {
    return item.startsWith(' ') && item.endsWith(' ')
}

CodePudding user response:

I would use the same approach from my answer to another (formerly asked) question of the OP ... "Transform array in to object with custom properties" ... and adapt it according to the OP's new requirements.

Thus one would stay with the reduce based approach which does not depend on outer scope references for keeping track of the currently to be built/aggregated property but instead makes this information part of the reducer function's first parameter, the previousValue which serves as an accumulator/collector object.

As for the OP's task, this collector would feature 2 properties, the sectionKey and the result, where the former holds the state of the currently processed property name, and the latter being the programmatically built result-array of section-items.

function aggregateStructuredTextItem(
  { sectionKey = '', result = [] },
  item
) {
  // try to retrieve the section specific text.
  let text = (item.match(/^\ \s*(.*?)\s*\ $/) || [])[1] ?? null;
  if (
    (text !== null) &&
    (text !== sectionKey)
  ) {
    // keep track of the currently processed section.    
    sectionKey = text;

    // create and collect new section entry.
    result.push({ section: text, content: [] });
  } else {
    // access the currently processed section.
    const section = result.at(-1);

    // try to retrieve the list specific text.
    text = (item.startsWith('-') && item.slice(1));
    if (text !== false) {

      // push list specific text into
      // the most recent content list.
      section.content.at(-1).list.push(text);
    } else {
      // create a content item and push it
      // into the most recent section content.
      section.content.push({ text: item, list: [] });
    }
  }
  return { sectionKey, result };
}

const textData = [
  ' section1 ',
  'big text 1 here.',
  'big text 2 here followed by list:',
  '-this is list item;',
  '-this is another list item;',
  ' section2 ',
  'big text 3 here followed by list:',
  '-this is list item;',
  '-this is another list item;',
  '-this is another list item;',
  ' section3 ', 
  'big text 4 here.'
];
const structuredTextData = textData
  .reduce(aggregateStructuredTextItem, { result: [] })
  .result;

console.log({ textData, structuredTextData });
.as-console-wrapper { min-height: 100%!important; top: 0; }

CodePudding user response:

Something like this should do it

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



const result = data.reduce((res, item) =>{
  if(item.startsWith(' ')){
    const section = item.substring(1, item.length -1)
    return[
        ...res,
       {
         section,
         content: []
       } 
      ]
  }
   const lastSectionIndex = res.length - 1
  if(item.startsWith('-')){
    const lastSection = res[lastSectionIndex]
    const lastContent = lastSection.content[lastSection.content.length -1]
    res[res.length - 1].content[lastSection.content.length -1].list.push(item.substring(1, item.length -1))
    return res
  }
  res[lastSectionIndex].content.push({text: item, list:[]})
 return res;

},[])

console.log(result)

  • Related