How to Group Element in Javascript to get the Cartesian product in javascript

Viewed 30

I have a scenario where I have an object just like this.

   [
    {attributeGroupId:2, attributeId: 11, name: 'Diamond'}
    {attributeGroupId:1, attributeId: 9, name: '916'}
    {attributeGroupId:1, attributeId: 1, name: '24K'}
    {attributeGroupId:2, attributeId: 12, name: 'Square'}
]

Expected result:

[
    {attributeGroupId:2, attributeId: 11, name: 'Diamond'},
    {attributeGroupId:2, attributeId: 12, name: 'Square'}
]

,

[
    {attributeGroupId:1, attributeId: 9, name: '916'},
    {attributeGroupId:1, attributeId: 1, name: '24K'}
]

So I can make the cartesian product of it just like this,

[
     {attributeId: 11-9, name: 'Diamond-916'}, 
     {attributeId: 11-1, name: 'Diamond-24K'},
     {attributeId: 12-9, name: 'Square-916'}, 
     {attributeId: 12-1, name: 'Square-24K'},
]

Now I want to keep this logic as generic as possible as the number of attributeGroupId is not known during runtime.

I think that splitting the array into multiple smaller array on the basis of attributeGroupId should be the first step.

1 Answers

You can implement a basic filter function like this:

function filterForAttributeGroupId(data, id) {
  return data.filter((item) => item.attributeGroupId === id);
}

console.log(filterForAttributeGroupId(data, 1)) //contains all elements with attributeGroupId = 1
console.log(filterForAttributeGroupId(data, 2)) //contains all elements with attributeGroupId = 2

Here you have a generic solution returning an array of filtered Arrays:

function filterForAttributeGroupId(data) {
  const mem = {};
  data.forEach((item) => {
    if ( mem[item.attributeGroupId] ) {
      mem[item.attributeGroupId].push(item);
    } else {
      mem[item.attributeGroupId] = [item];
    }
  })
  return Object.values(mem);
}

Edit after feedback from comment If the order of concatenated attributes does not matter, you can use the following code to get the "cartesian concatenation" of n different arrays:

function cartesianProduct(arrays) {
  if (arrays.length <= 1 ) return arrays[0];
  const first = arrays[0];
  const second = cartesianProduct(arrays.slice(1));
  const result =  [];
   first.forEach(( itemFirst ) => {
      second.forEach( (itemSecond) => {
        result.push({attributeId: `${itemFirst.attributeId}-${itemSecond.attributeId}`, name: `${itemFirst.name}-${itemSecond.name}`})
      });
   });
   return result;
}

This way calling the following:

console.log(cartesianProduct(filterForAttributeGroupId(data)));

results in the expected data (although the strings are concatenated in another order):

[
  {
    "attributeId":"9-11",
    "name":"916-Diamond"
  },
  {
    "attributeId":"9-12",
    "name":"916-Square"
  },
  {
    "attributeId":"1-11",
    "name":"24K-Diamond"
  },
  {
    "attributeId":"1-12",
    "name":"24K-Square"
  }
]
Related