Home > Software design >  In array of strings split items (into key-value pairs) and group by common key
In array of strings split items (into key-value pairs) and group by common key

Time:11-03

I'm using Angular, and I have input data in the following structure, key = string, value = array of strings:

  inputObject= {
'employees': [
  'bob/january',
  'bob/january-february',
  'bob/january-march',
  'steve/january',
  'steve/january-february',
  'steve/january-march',
  'september',
],

};

I need to transform this object by splitting the values using '/' as a delimiter, so splitting the string elements in the employees array and put that in a new object where the string value before the '/' becomes the key of the new object and the string values after the '/'are added as an array of string values. if the value in the employees element does not have a '/' in the string then the key should be a hardcoded string value such as 'n/a' as seen below

Desired Output:

  outputObject = {
'bob': [
  'january',
  'january-february',
  'january-march'
],
'steve': [
  'january',
  'january-february',
  'january-march',
],
'n/a': 'september'

}

I have tried various techniques, one such example as seen in the snippet where I attempted to add to a map. This would work by adding to a new object or any other data structure that you could recommend.

inputObject= {
    'employees': [
      'bob/january',
      'bob/january-february',
      'bob/january-march',
      'steve/january',
      'steve/january-february',
      'steve/january-march',
      'september',
    ],
  };

  let outputMap = new Map();
  
  let splitValues = inputObject.employees.map(a => a.split('/'));
   console.log(splitValues)
  
  splitValues.forEach(a => {
    if(a.length > 1){
    outputMap.set(a[0], a[1]);
    } else {
    outputMap.set('n/a', a[0])
    }
    
  });
  
  console.log(outputMap)
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Stackblitz: https://stackblitz.com/edit/angular-ivy-ogbdwv

CodePudding user response:

You may use Array.prototype.reduce() to group items and String.prototype.match() to implement splitting logic:

const input = {
          employees: [
          'bob/january',
          'bob/january-february',
          'bob/january-march',
          'steve/january',
          'steve/january-february',
          'steve/january-march',
          'september',
        ]
      },
      
      
      output = input.employees
        .reduce((acc, str) => {
          const [key = 'n/a', value] = str
            .match(/(([^/] )\/)?(. )/)
            .slice(2)
          const group = acc[key]
          group
            ? group.push(value)
            : acc[key] = [value]
          return acc
        }, {})
      
console.log(output)
.as-console-wrapper{min-height:100%;}
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

Reduce is perfect for changing one data structure to another.

It might look like this (untested):

type Output = {
  [name:string]: string[]
}

const output = inputObject.employees.reduce<Output>((acc, curr) => {
  const splitStr = curr.split('/')

  if (splitStr.length === 1) {
    return {
      ...acc,
      'n/a': [
        ...acc['n/a'],
        splitStr[0]
      ]
    }
  }

  const [name, date] = splitStr;

  return {
    ...acc,
    [name]: acc[name]?.length > 0
      ? [...acc[name], date]
      : [date]
  }
}, {});

console.log(output);
  • Related