Home > front end >  Javascript: array of objects, group/restructure objects
Javascript: array of objects, group/restructure objects

Time:11-30

The following object contains a list of recipients and subscriptions, I wish to create a new array with a different structure such as below.

[{"recipientId":"13251376", "services":"3218143,15656200,3721"},{"recipientId":"13251316", "services":"3218143"}

let serviceSubscriptions =  [{"recipientId":"13251316","serviceId":"3218143"},{"recipientId":"13251376","serviceId":"3218143"},{"recipientId":"13251376","serviceId":"15656200"},{"recipientId":"13251376","serviceId":"3721"}]

let testArr = [];

serviceSubscriptions.forEach(serviceSubscriptions => {
  for (const [key, value] of Object.entries(serviceSubscriptions)) {
    //console.log(`${key}: ${value}`);
    testArr.push(`${key}:${value}`);
    
  }
});
console.log(testArr);

Here is a list of things I've tried - https://jsfiddle.net/v5azdysg/2/

CodePudding user response:

You can reduce each item into a Map<String, Set<String>>. After you have the map, you can map the entries to objects where you join the serviceId arrays.

const serviceSubscriptions =  [
  { "recipientId": "13251316", "serviceId": "3218143"  },
  { "recipientId": "13251376", "serviceId": "3218143"  },
  { "recipientId": "13251376", "serviceId": "15656200" },
  { "recipientId": "13251376", "serviceId": "3721"     }
];

const testArr = [
  ...serviceSubscriptions
    .reduce((acc, { recipientId, serviceId }) =>
      acc.set(recipientId,
        (acc.get(recipientId) ?? new Set).add(serviceId)), new Map)
    .entries()
  ]
  .map(([recipientId, serviceIds]) => ({
    recipientId,
    services: [...serviceIds].join(',')
  }));

console.log(testArr);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Here is an alternative version that uses Object and Array types instead of Map and Set types.

Note: This performs worse than the version above, because it uses spreading which mutates the accumulator and service array values. This should be avoided.

const serviceSubscriptions =  [
  { "recipientId": "13251316", "serviceId": "3218143"  },
  { "recipientId": "13251376", "serviceId": "3218143"  },
  { "recipientId": "13251376", "serviceId": "15656200" },
  { "recipientId": "13251376", "serviceId": "3721"     }
];

const testArr = Object
  .entries(serviceSubscriptions
    .reduce((acc, { recipientId, serviceId }) => ({
      ...acc,
      [recipientId]: [...(acc[recipientId] ?? []), serviceId]
    }), {}))
  .map(([recipientId, serviceIds]) => ({
    recipientId,
    services: serviceIds.join(',')
  }));

console.log(testArr);
.as-console-wrapper { top: 0; max-height: 100% !important; }

CodePudding user response:

There are many ways to solve this, Mr. Polywhirl's answer is more elegant than the following (I haven't done the O() calc on it to determine efficiency vs this one), but a basic solution just needs to use a couple for loops.

One to get the elements, another to check the testArr to see if we already handled the recipient and another to iterate over the array again starting from the next index to add the subscription ids.

let serviceSubscriptions =  [{"recipientId":"13251316","serviceId":"3218143"},{"recipientId":"13251376","serviceId":"3218143"},{"recipientId":"13251376","serviceId":"15656200"},{"recipientId":"13251376","serviceId":"3721"}]

let testArr = [];

for(let i = 0; i < serviceSubscriptions.length; i  ) {
  // Create a new object so we don't alter the original.
  const recipSub = Object.assign({}, serviceSubscriptions[i]);
  if (testArr.filter(r => r.recipientId === recipSub.recipientId).length > 0) continue;
   for (let y = i   1; y < serviceSubscriptions.length; y  ) {
      if (serviceSubscriptions[y].recipientId === recipSub.recipientId) {
  recipSub.serviceId = recipSub.serviceId   ','   serviceSubscriptions[y].serviceId;

}
   }
testArr.push(recipSub);
}
console.log(testArr);
console.log(serviceSubscriptions);

  • Related