Home > Software engineering >  How do I create an array of objects with a nested array based on a similar key?
How do I create an array of objects with a nested array based on a similar key?

Time:08-27

I have an array that looks something like this

const example = [
  { id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
  { id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
  { id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];

As you can see, the organization name is something I want to key off of and create a data structure like this:

const output = [
  // data.value will be their ID
  { 
    organizationName: 'Organization A', 
    data: [
      { label: 'Person 1', value: '1' },
      { label: 'Person 2', value: '2' },
    ],
  },
  { 
    organizationName: 'Organization B', 
    data: [
      { label: 'Person 3', value: '3' },
    ],
  },
]

What I've tried

I know I want to use reduce for something like this, but I feel like I'm off:

const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
  const {
    organization: { name: organizationName },
  } = currentValue;
  if (organizationName) {
    acc.push({ organization: organizationName, data: [] });
  } else {
    const { name: externalPersonName, id } = currentValue;
    acc[acc.length - 1].data.push({ name: externalPersonName, value: id });
  }
  return acc;
}, [] as any);

However the output comes out to something like this:

[
  {organizationName: 'Organization A', data: []},
  {organizationName: 'Organization A', data: []},
  {organizationName: 'Organization B', data: []},
];

data doesn't seem to get anything pushed inside the array in this reduce function, and the organization name get duplicated... what am I doing wrong?

CodePudding user response:

Easiest way is to use an Map/Set/or object to keep track of orgs you create. This way you are not searching in the array to see if the organization was found already. After you are done, you can create the array you want from the object.

const externalPeople = { 
  data : [
    { id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
    { id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
    { id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
  ],
};

const providerOptions = Object.values(externalPeople.data.reduce((acc, currentValue) => {
  const {
    organization: { name: organizationName },
    name: externalPersonName,
    id
  } = currentValue;

  // Is the org new? Yes, create an entry for it
  if (!acc[organizationName]) {
    acc[organizationName] = { organization: organizationName, data: [] };
  }

  // push the person to the organization
  acc[organizationName].data.push({ name: externalPersonName, value: id });

  return acc;
}, {}));

console.log(providerOptions)

CodePudding user response:

Here is another solution

const example = [
  { id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
  { id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
  { id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];

const result = example.reduce((res, entry) => {
 const recordIndex = res.findIndex(rec => rec.organizationName === entry.organization.name);
 if(recordIndex >= 0) {
   res[recordIndex].data.push({ label: entry.name, value: entry.id});
 } else {
   const record = {
     organizationName: entry.organization.name,
     data: [{ label: entry.name, value: entry.id }]
   };
   res.push(record);
 }
  return res;
}, []);
  console.log(result);

CodePudding user response:

You are not checking if the value is already present in your accumulation acc

You can check it with a simple find in the if statement since it's an array

const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
    const {
        organization: { name: organizationName },
    } = currentValue;

    //Check if organization is not present already
    if (!acc.find(a => a.organization === organizationName)) {
        //Add also the data of the element your are processing
        acc.push({ organization: organizationName, data: [{label: currentValue.name, value: currentValue.id}] });
    } else {
        const { name: externalPersonName, id } = currentValue;
        acc[acc.length - 1].data.push({ label: externalPersonName, value: id });
    }
    return acc;
}, [] as any);

I also added the data of the first element of the group you create when adding the organization.

The result should be as your expected output:

[
  {
    organization: 'Organization A',
    data: [
      { label: 'Person 1', value: '1' },
      { label: 'Person 2', value: '2' }
    ]
  },
  {
    organization: 'Organization B',
    data: [
      { label: 'Person 3', value: '3' }
    ]
  }
]

Hope it helps!

CodePudding user response:

Something like that with using a Set?

result = [...new Set(example.map(d => d.organization.name))].map(label => {
  return {
    organizationName: label,
    data: example.filter(d => d.organization.name === label).map(d => {
      return {label: d.name, value: d.id}
    })
  }
})

`

  • Related