1

I have an array of objects that i need to flatten/simplify/combine based on certain conditions. Here is the shape of my current array of objects below:

const arrayOfObjects = 
  [ { Battery           : 'Battery'        } 
  , { batteryDetailsKey : [ 'Serial Number', 'Type', 'Part Number'       ] } 
  , { batteryDetailsVal : [ 'HJ3CA19347410218LJ98 100 QC', 'Extended Range', '4P94-Q001' ] } 
  , { Modules           : 'Modules'        } 
  , { moduleDetailsKey  : [ 'Serial Number', 'Part Number', 'Cell Count' ] } 
  , { moduleDetailsVal  : [ '83675327350061093222581609899001', 'LJ98-10C779-A01', '32'  ] } 
  , { assetSeparator    : 'assetSeparator' } 
  , { Battery           : 'Battery'        }  
  , { batteryDetailsKey : [ 'Serial Number', 'Type', 'Part Number'       ] } 
  , { batteryDetailsVal : [ 'HJ3CA19347410218LJ98 101 QC', 'Extended Range', '4P94-Q002' ] } 
  , { Modules           : 'Modules'        } 
  , { moduleDetailsKey  : [ 'Serial Number', 'Part Number', 'Cell Count' ] } 
  , { moduleDetailsVal  : [ '83675327350061093222581609899002', 'LJ98-10C779-A02', '28'  ] } 
  , { moduleDetailsVal  : [ '83675327350061093222581609899003', 'LJ98-10C779-A03', '27'  ] } 
  , { assetSeparator    : 'assetSeparator' } 
  , { Battery           : 'Battery'        } 
  , { batteryDetailsKey : [ 'Serial Number', 'Type', 'Part Number'                       ] } 
  , { batteryDetailsVal : [ 'HJ3CA19347410218LJ98 102 QC', 'Extended Range', '4P94-Q003' ] } 
  , { Modules           : 'Modules'        } 
  , { moduleDetailsKey  : [ 'Serial Number', 'Part Number', 'Cell Count' ] } 
  , { moduleDetailsVal  : [ '83675327350061093222581609899004', 'LJ98-10C779-A01', '32'  ] } 
  ] ]

I basically want this arrayOfObjects to be shaped into this structure:

const shapeIWantArrayOfObjects = 
  [ { Battery           : 'Battery'
    , batteryDetailsKey : [ 'Serial Number', 'Type', 'Part Number' ] 
    , batteryDetailsVal : [ 'HJ3CA19347410218LJ98 100 QC', 'Extended Range', '4P94-Q001' ] 
    , Modules           : 'Modules'
    , moduleDetailsKey  : [ 'Serial Number', 'Part Number', 'Cell Count' ] 
    , moduleDetailsVal  : [ '83675327350061093222581609899001', 'LJ98-10C779-A01', '32' ] 
    } 
  , { Battery           : 'Battery'
    , batteryDetailsKey : [ 'Serial Number', 'Type', 'Part Number' ] 
    , batteryDetailsVal : [ 'HJ3CA19347410218LJ98 101 QC', 'Extended Range', '4P94-Q002' ] 
    , Modules           : 'Modules'
    , moduleDetailsKey  : [ 'Serial Number', 'Part Number', 'Cell Count'] 
    , moduleDetailsVal  : [ '83675327350061093222581609899002', 'LJ98-10C779-A02', '28' ] 
    , moduleDetailsVal  : [ '83675327350061093222581609899003', 'LJ98-10C779-A03', '27' ] 
    } 
  , { Battery           : 'Battery'
    , batteryDetailsKey : [ 'Serial Number', 'Type', 'Part Number' ] 
    , batteryDetailsVal : [ 'HJ3CA19347410218LJ98 102 QC', 'Extended Range', '4P94-Q003' ] 
    , Modules           : 'Modules'
    , moduleDetailsKey  : [ 'Serial Number', 'Part Number', 'Cell Count'] 
    , moduleDetailsVal  : [ '83675327350061093222581609899004', 'LJ98-10C779-A01', '32' ] 
    } 
  ] 

As you can see i'm basically wanting to combine the modules and batteries details into one object, then as you can see i want to create another object within that array once i hit the {"assetSeparator": "assetSeparator"}. That's like my conditional that tells me that asset has been combined, now time to combine the next one, almost think of it as string.split("assetSeparator")

Can someone please tell me how i could achieve this, i've tried Object.assign({}, ...arrayOfObjects) but that didn't quite achieve what i want, and i can't detect the {"assetSeparator": "assetSeparator"} using the spread operator.

I've also tried doing a reduce arrayOfObjects.reduce(function(result, current) { return Object.assign(result, current); }, {}) but because it's accumulating an object, it's just override object properties with the same keys. Please help.

10
  • does each group of items in the original array always start with "Battery": "Battery" object? I.e. an object with exactly one key called "Battery" Commented Mar 16, 2022 at 0:53
  • 3
    What a weird way of structuring data! Commented Mar 16, 2022 at 0:58
  • 2
    what you are asking is not possible, there cannot be two properties with identical name in javascript or json objects (moduleDetailsVal) Commented Mar 16, 2022 at 1:41
  • Oh, I didn't see that moduleDetailsVal was repeated in a battery - easy fix, but of course the output can not be as expected Commented Mar 16, 2022 at 1:46
  • @Ram i didn't structure it this way, i'm reading it off a CSV, then trying to build the object afterwards Commented Mar 16, 2022 at 2:12

4 Answers 4

3

Please note, that your required output is IMPOSSIBLE - but if there's more than one object with the same key, the output could have an array of values for that key - see code

If each group of items in the original array begins with an object with a single key "Battery" - then this will perform the majicks for you

const arrayOfObjects = [{"Battery": "Battery"},{"batteryDetailsKey": ["Serial Number","Type","Part Number",]},{"batteryDetailsVal": ["HJ3CA19347410218LJ98 100 QC","Extended Range","4P94-Q001",]},{"Modules": "Modules"},{"moduleDetailsKey": ["Serial Number","Part Number","Cell Count",]},{"moduleDetailsVal": ["83675327350061093222581609899001","LJ98-10C779-A01","32",]},{"assetSeparator": "assetSeparator"},{"Battery": "Battery"},{"batteryDetailsKey": ["Serial Number","Type","Part Number"]},{"batteryDetailsVal": ["HJ3CA19347410218LJ98 101 QC","Extended Range","4P94-Q002"]},{"Modules": "Modules"},{"moduleDetailsKey": ["Serial Number","Part Number","Cell Count"]},{"moduleDetailsVal": ["83675327350061093222581609899002","LJ98-10C779-A02","28"]},{"moduleDetailsVal": ["83675327350061093222581609899003","LJ98-10C779-A03","27"]},{"assetSeparator": "assetSeparator"},{"Battery": "Battery"},{"batteryDetailsKey": ["Serial Number","Type","Part Number",]},{"batteryDetailsVal": ["HJ3CA19347410218LJ98 102 QC","Extended Range","4P94-Q003",]},{"Modules": "Modules"},{"moduleDetailsKey": ["Serial Number","Part Number","Cell Count",]},{"moduleDetailsVal": ["83675327350061093222581609899004","LJ98-10C779-A01","32",]}];

const shapeIWantArrayOfObjects = [];
let currentOutput;
for (let object of arrayOfObjects) {
    if (Object.keys(object).join('') === 'Battery') {
        currentOutput = {};
        shapeIWantArrayOfObjects.push(currentOutput);
    }
    Object.entries(object).forEach(([key, val]) => {
        const existing = currentOutput[key];
        if (!existing) {
            currentOutput[key] = val;
        } else {
            if (!Array.isArray(currentOutput[key][0])) {
                currentOutput[key] = [currentOutput[key]];
            }
            currentOutput[key].push(val);
        }
    });
}
console.log(shapeIWantArrayOfObjects);
.as-console-wrapper {max-height: 100%!important; top:0; }
.as-console-row::after { display:none !important; }

Sign up to request clarification or add additional context in comments.

8 Comments

Good answer Johnny! :)
@Ram - it's a pretty answer :p (you'll understand if you know Johnny Bravo)
Yeah, "it's smells kinda pretty" :)
@MisterJojo Oh, I didn't see that moduleDetailsVal was repeated in a battery - easy fix though
no, you will not be able to fix this, its data model is wrong, no JS object can have 2 identical properties, only the last one is referenced
|
0

You can group them based on the key of an object i.e. assetSeparator

const result = arrayOfObjects.reduce((acc, curr) => {
  if (new Set(Object.keys(curr)).has('assetSeparator')) {
    acc.push({});
  } else {
    if (!acc.length) acc.push({ ...curr });
    else {
      const last = acc[acc.length - 1];
      Object.keys(curr).forEach((k) => {
        last[k] = curr[k];
      });
    }
  }
  return acc;
}, []);

const arrayOfObjects = [
  {
    Battery: 'Battery',
  },
  {
    batteryDetailsKey: ['Serial Number', 'Type', 'Part Number'],
  },
  {
    batteryDetailsVal: [
      'HJ3CA19347410218LJ98 100 QC',
      'Extended Range',
      '4P94-Q001',
    ],
  },
  {
    Modules: 'Modules',
  },
  {
    moduleDetailsKey: ['Serial Number', 'Part Number', 'Cell Count'],
  },
  {
    moduleDetailsVal: [
      '83675327350061093222581609899001',
      'LJ98-10C779-A01',
      '32',
    ],
  },
  {
    assetSeparator: 'assetSeparator',
  },

  {
    Battery: 'Battery',
  },
  {
    batteryDetailsKey: ['Serial Number', 'Type', 'Part Number'],
  },
  {
    batteryDetailsVal: [
      'HJ3CA19347410218LJ98 101 QC',
      'Extended Range',
      '4P94-Q002',
    ],
  },
  {
    Modules: 'Modules',
  },
  {
    moduleDetailsKey: ['Serial Number', 'Part Number', 'Cell Count'],
  },
  {
    moduleDetailsVal: [
      '83675327350061093222581609899002',
      'LJ98-10C779-A02',
      '28',
    ],
  },
  {
    moduleDetailsVal: [
      '83675327350061093222581609899003',
      'LJ98-10C779-A03',
      '27',
    ],
  },
  {
    assetSeparator: 'assetSeparator',
  },
  {
    Battery: 'Battery',
  },
  {
    batteryDetailsKey: ['Serial Number', 'Type', 'Part Number'],
  },
  {
    batteryDetailsVal: [
      'HJ3CA19347410218LJ98 102 QC',
      'Extended Range',
      '4P94-Q003',
    ],
  },
  {
    Modules: 'Modules',
  },
  {
    moduleDetailsKey: ['Serial Number', 'Part Number', 'Cell Count'],
  },
  {
    moduleDetailsVal: [
      '83675327350061093222581609899004',
      'LJ98-10C779-A01',
      '32',
    ],
  },
];

const result = arrayOfObjects.reduce((acc, curr) => {
  if (new Set(Object.keys(curr)).has('assetSeparator')) {
    acc.push({});
  } else {
    if (!acc.length) acc.push({ ...curr });
    else {
      const last = acc[acc.length - 1];
      Object.keys(curr).forEach((k) => {
        last[k] = curr[k];
      });
    }
  }
  return acc;
}, []);

console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }

3 Comments

Sort of wrong answer, moduleDetailsVal: [ '83675327350061093222581609899002', 'LJ98-10C779-A02', '28' ] why is this line not in your result?
@MisterJojo Where is in the source array? I'm not able to find and understand what is wrong here.
moduleDetailsVal is repeated in a battery
0

You can try something like this

const arrayOfObjects = [{"Battery": "Battery"}, {"batteryDetailsKey": ["Serial Number", "Type", "Part Number", ] }, {"batteryDetailsVal": ["HJ3CA19347410218LJ98 100 QC", "Extended Range", "4P94-Q001", ] }, {"Modules": "Modules"}, {"moduleDetailsKey": ["Serial Number", "Part Number", "Cell Count", ] }, {"moduleDetailsVal": ["83675327350061093222581609899001", "LJ98-10C779-A01", "32", ] }, {"assetSeparator": "assetSeparator"}, {"Battery": "Battery"}, {"batteryDetailsKey": ["Serial Number", "Type", "Part Number"] }, {"batteryDetailsVal": ["HJ3CA19347410218LJ98 101 QC", "Extended Range", "4P94-Q002"] }, {"Modules": "Modules"}, {"moduleDetailsKey": ["Serial Number", "Part Number", "Cell Count"] }, {"moduleDetailsVal": ["83675327350061093222581609899002", "LJ98-10C779-A02", "28"] }, {"moduleDetailsVal": ["83675327350061093222581609899003", "LJ98-10C779-A03", "27"] }, {"assetSeparator": "assetSeparator"}, {"Battery": "Battery"}, {"batteryDetailsKey": ["Serial Number", "Type", "Part Number", ] }, {"batteryDetailsVal": ["HJ3CA19347410218LJ98 102 QC", "Extended Range", "4P94-Q003", ] }, {"Modules": "Modules"}, {"moduleDetailsKey": ["Serial Number", "Part Number", "Cell Count", ] }, {"moduleDetailsVal": ["83675327350061093222581609899004", "LJ98-10C779-A01", "32", ] }];

let shapeIWantArrayOfObjects = [], tempObj = {}, i = 0;
arrayOfObjects.forEach(obj => {
    if( !obj.hasOwnProperty('assetSeparator') ){
        shapeIWantArrayOfObjects[i] = { ...shapeIWantArrayOfObjects[i], ...obj };
    }else{
        i++;
        tempObj = {};
    }
});

console.log(shapeIWantArrayOfObjects)

3 Comments

Sort of wrong answer, moduleDetailsVal: [ '83675327350061093222581609899002', 'LJ98-10C779-A02', '28' ] why is this line not in your result?
@MisterJojo it cannot be part because, the keys of an object must be unique. From the OP's expected result, moduleDetailsVal was added twice which is invalid.
yes, I agree, no solution is neither possible nor valid
0

Another one solution. You can use hash grouping approach to combine objects by condition.

const arrayOfObjects = [{"Battery":"Battery"},{"batteryDetailsKey":["Serial Number","Type","Part Number"]},{"batteryDetailsVal":["HJ3CA19347410218LJ98 100 QC","Extended Range","4P94-Q001"]},{"Modules":"Modules"},{"moduleDetailsKey":["Serial Number","Part Number","Cell Count"]},{"moduleDetailsVal":["83675327350061093222581609899001","LJ98-10C779-A01","32"]},{"assetSeparator":"assetSeparator"},{"Battery":"Battery"},{"batteryDetailsKey":["Serial Number","Type","Part Number"]},{"batteryDetailsVal":["HJ3CA19347410218LJ98 101 QC","Extended Range","4P94-Q002"]},{"Modules":"Modules"},{"moduleDetailsKey":["Serial Number","Part Number","Cell Count"]},{"moduleDetailsVal":["83675327350061093222581609899002","LJ98-10C779-A02","28"]},{"moduleDetailsVal":["83675327350061093222581609899003","LJ98-10C779-A03","27"]},{"assetSeparator":"assetSeparator"},{"Battery":"Battery"},{"batteryDetailsKey":["Serial Number","Type","Part Number"]},{"batteryDetailsVal":["HJ3CA19347410218LJ98 102 QC","Extended Range","4P94-Q003"]},{"Modules":"Modules"},{"moduleDetailsKey":["Serial Number","Part Number","Cell Count"]},{"moduleDetailsVal":["83675327350061093222581609899004","LJ98-10C779-A01","32"]}];

let objectCount = 0;
const shapeIWantArrayOfObjects = Object.values(arrayOfObjects.reduce((acc, item) => {
  if (item.assetSeparator === "assetSeparator") {
    objectCount += 1;
    return acc;
  }
  acc[objectCount] ??= {};
  acc[objectCount] = { ...acc[objectCount], ...item };
  
  return acc;
}, {}));

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.