-2

I've tried various answers to this (same/similar) question here, and none have given the desired result. I have a flat Json file that I need to convert to nested. The first item in each chunk is the item that the data needs grouping on when converted to nested data. The example data just shows two "types" but there are several more in the data, and a few more other elements along with colour and size as well - I kept it short for posting here.

Example data input:

[
  {
    "type": "Background",
    "colour": "Blue",
    "size": "5"
  },
  {
    "type": "Background",
    "colour": "Grey",
    "size": "3"
  },
  {
    "type": "Foreground",
    "colour": "Red",
    "size": "5"
  },
  {
    "type": "Foreground",
    "colour": "White",
    "size": "10"
  }
]

Desired Output:

[
  {
    "type": "Background",
    "elements": [
      {
        "colour": "Blue",
        "size": "5"
      },
      {
        "colour": "Grey",
        "size": "3"
      }
    ]
  }
  {
    "type": "Foreground",
    "elements": [
      {
        "colour": "Red",
        "size": "5"
      },
      {
        "colour": "White",
        "size": "10"
      }
    ]
  }
]

The closest I came to an answer for this was here: Create nested json from flattened JSON in javascript

But the output I received from that (in VSCode) was this:

{
  type: { elements: [ [Object], [Object] ] }
}

While I am trying to follow the code, it is beyond my javascript knowledge currently, so I don't know what's going wrong, or why I get [object] and not the actual elements?

Can anyone help me get this sorted out so my output looks like my above "Desired output"?

Cheers!

robgt
  • 767
  • 3
  • 14
  • 31

3 Answers3

1

You can use the forEach() method to loop over the array, in this case, result. If the type wasn't set in the result, then add the new type. But, if the item already has its type in the result, then just push the item's elements.

const items = [
  {
    type: 'Background',
    colour: 'Blue',
    size: '5',
  },
  {
    type: 'Background',
    colour: 'Grey',
    size: '3',
  },
  {
    type: 'Foreground',
    colour: 'Red',
    size: '5',
  },
  {
    type: 'Foreground',
    colour: 'White',
    size: '10',
  },
];

let result = [];

items.forEach((item) => {
  if (!result.find((x) => x.type === item.type)) {
    result.push({ type: item.type, elements: [] });
  }

  result
    .find((x) => x.type === item.type)
    .elements.push({
      color: item.colour,
      size: item.size,
    });
});

console.log(result);

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

amlxv
  • 1,610
  • 1
  • 11
  • 18
  • Thanks so much for the help! This works almost perfectly, but there seems to be some additional loop occurring, so I end up with extra blank Json arrays below the populated ones for the same type - i.e. if there are 3 background objects, those 3 are output correctly formatted, but also followed by 2 more empty background objects, before moving on to the next "type"? – robgt Feb 06 '22 at 14:34
1

try this

var output = [];

var newObj = { type: input[0].type, elements: [] };
var prevType = input[0].type;

input.forEach((element) => {
  if (element.type != prevType) {
    output.push(newObj);
    newObj = { type: element.type, elements: [] };
    prevType = element.type;
  }
  newObj.elements.push({ colour: element.colour, size: element.size });
});
output.push(newObj);
Serge
  • 40,935
  • 4
  • 18
  • 45
0

It seems that there are two steps here. First group all entries with the same type (which would be easier under the type name as a property, then build the nesting.

const input = 
    [
      {
        "type": "Background",
        "colour": "Blue",
        "size": "5"
      },
      {
        "type": "Background",
        "colour": "Grey",
        "size": "3"
      },
      {
        "type": "Foreground",
        "colour": "Red",
        "size": "5"
      },
      {
        "type": "Foreground",
        "colour": "White",
        "size": "10"
      }
    ];

var assist = {},
    output = [];

// Group original objects based on type
// The type is used as a property value
for (let i = 0; i < input.length; i++) {
  let type = input[i].type;
  
  if (assist.hasOwnProperty(type)) {
    assist[type].push(input[i]);
  } else {
    assist[type] = [input[i]];
  }
  delete input[i].type;
}


// Turn the grouped entries into new array entries
let keys = Object.keys(assist);
for (let i = 0; i < keys.length; i++) {
  output.push({
    type: keys[i],
    elements: assist[keys[i]]
  });
};

// Show results
console.log(output);

Note that while the above code does not modify the original array, it does modify it's members (deleting their type property) and using them in the output array. You will have to clone it if you need the original too.

Noam
  • 1,317
  • 5
  • 16