Brett B
Brett B

Reputation: 53

dynamically flatten nested array of objects javascript

I'm trying to write a function that will accept a nested object array, and dynamically return the flattened result. arrayProperties.filter() is not returning an array of objects like I expect.

const data = [ 
    {
        parKeyA: "parValA",
        parKeyA1:
            {chiKeyA1: "chiValA1", chiKeyA2: "chiValA2"},
        parKeyA2: {chiKeyA3: "chiValA3"}
    },
    {
        parKeyB: "parValB",
        parKeyB1:
            {chiKeyB1:"chiValB1"}
    }
]

flatData = flatNestedObjArray(data);
console.log(flatData);

function flatNestedObjArray(array) {
    let flatArray = array.map(element => {
        let arrayProperties = Object.entries(element);
        //filter not returning array of objects
        let nestedObjects = arrayProperties.filter(property => {
            const parentValue = property[1];
            if (typeof parentValue === "object" && parentValue !== null) {
                return parentValue;
            }
        });
        //nestedObjects should be array of objects
        let merged = nestedObjects.map(obj => element.concat(obj));
        return merged;
    });
    return flatArray;
}

Expected Result:

const data = [ 
    {
        parKeyA: "parValA",
        chiKeyA1: "chiValA1",
        chiKeyA2: "chiValA2",
        chiKeyA2: "chiValA2"
    },
    {
        parKeyB: "parValB",
        chiKeyB1:"chiValB1"
    }
]

Upvotes: 2

Views: 3033

Answers (4)

zer00ne
zer00ne

Reputation: 43880

Object.entries() takes an object and converts it into a two-dimensional array:

let object = {keyA: "valueA", keyB: "valueB", keyC: {kA: "vA", kB: "vB"}};
let array = Object.entries(object);
// array = [["keyA", "valueA"], ["keyB", "valueB"], ["keyC", {kA: "vA", kB: "vB"}]];

Using the above within a for...of loop, each entry can be destructured:

for (let [key, value] of Object.entries(object)) {...

  1. Declare an empty array and iterate through each object literal within the array of objects:

    let array = [];
    for (let obj of objArray) {...
    
  2. On each object, declare an empty object and then convert each key/value of each object into a sub-array:

    let object = {};
    for (let [key, value] of Object.entries(obj)) {...
    
  3. Check each value of each object literal -- if the value is an object literal...

    if (Object.prototype.toString.call(value) == "[object Object]") {...
    
  4. ...iterate through the value and assign each key/value to the empty object...

    for (let [k, v] of Object.entries(value)) {
      object[k] = v;
    }
    
  5. ...otherwise assign the key/value to the empty object...

    } else {
      object[key] = value;
    }
    
  6. Push the new object to the new array:

    array.push(object);
    

Demo

const data = [{
    parKeyA: "parValA",
    parKeyA1: {
      chiKeyA1: "chiValA1",
      chiKeyA2: "chiValA2"
    },
    parKeyA2: {
      chiKeyA3: "chiValA3"
    }
  },
  {
    parKeyB: "parValB",
    parKeyB1: {
      chiKeyB1: "chiValB1"
    }
  }
];

function subObjToKeyVal(objArr) {
  let array = [];
  for (let obj of objArr) {
    let object = {};
    for (let [key, value] of Object.entries(obj)) {
      if (Object.prototype.toString.call(value) == "[object Object]") {
        for (let [k, v] of Object.entries(value)) {
          object[k] = v;
        }
      } else {
        object[key] = value;
      }
    }
    array.push(object);
  }
  return array;
}

console.log(subObjToKeyVal(data));

Upvotes: 0

brk
brk

Reputation: 50291

You can use map which will return a array and a recursive function. Have added comment in the code , hopefully that will be useful

const data = [{
    parKeyA: "parValA",
    parKeyA1: {
      chiKeyA1: "chiValA1",
      chiKeyA2: "chiValA2"
    },
    parKeyA2: {
      chiKeyA3: "chiValA2"
    }
  },
  {
    parKeyB: "parValB",
    parKeyB1: {
      chiKeyB1: "chiValB1"
    }
  }
]
/* Recursive function.It will take a object,iterate the object and check if the value of the key is another object. If it is another object then call same recursive function  */
function getFlatObj(obj) {
  let newObject = {}

  function doRecurssion(currObj) {
    // iterate through the object
    for (let keys in currObj) {
      // check if the value is another object 
      if (typeof currObj[keys] === 'object' && typeof currObj[keys] !== null) {
        doRecurssion(currObj)
      } else {
        // if not another object then add key and value
        newObject[keys] = currObj[keys]

      }
    }
    return newObject;
  }

  return doRecurssion(obj);
}

let flatObj = data.map((item) => {
  const acc = {};
  for (let keys in item) {
    if (typeof item[keys] === 'object' && typeof item[keys] !== null) {
      Object.assign(acc, getFlatObj(item[keys]))
    } else {
      acc[keys] = item[keys]
    }
  }

  return acc;
}, {});

console.log(flatObj)

Upvotes: 1

Code Maniac
Code Maniac

Reputation: 37755

You can use recursion to flatten the objects into a single level object and pass that function to map to get an array of flattened object

const data = [{
    parKeyA: "parValA",
    parKeyA1: {
      chiKeyA1: "chiValA1",
      chiKeyA2: "chiValA2"
    },
    parKeyA2: {
      chiKeyA3: "chiValA3"
    }
  },
  {
    parKeyB: "parValB",
    parKeyB1: {
      chiKeyB1: "chiValB1",
      chiKeyB2: {}
    }
  }
]


let flatten = (obj, final = {}) => {
  for (let key in obj) {
    if (typeof obj[key] === 'object' && obj[key] != null) {
      flatten(obj[key], final)
    } else {
      final[key] = obj[key]
    }
  }
  return final
}

console.log(data.map((v) => flatten(v)))

Upvotes: 3

Dickens A S
Dickens A S

Reputation: 4054

You can use object property loop using in keyword for each level using recursion

for(var prop in data) {
    ....
}

I used an old recursion technique to start with a working code

function flatten(data) {
    var newData = {};
    for(var prop in data) {
        if(typeof data[prop] == "object") {
            var childs = flatten(data[prop])
            for(var cprop in childs){
                newData[cprop] = childs[cprop];
            }
        }else {
            newData[prop] = data[prop]
        }
    }
    return newData;
}

for(var i=0;i<data.length;i++)
   data[i] = flatten(data[i]);

console.log(data);

You need to handle duplicates

Upvotes: 1

Related Questions