Deep flatten object in Javascript – 1

Given an nested object which can have any type of object, deep flatten it and return the new object in Javascript.

For example

Input:
{
  A: "12",
  B: 23,
  C: {
    P: 23,
    O: {
       L: 56
    },
    Q: [1, 2]
   }   
}

Output:
{
  "A": "12"
  "B": 23,
  "C.O.L": 56,
  "C.P": 23,
  "C.Q.0": 1,
  "C.Q.1": 2,
}

Deep flatten object Javascript

In the output if you notice, when we have nested objects the key is concatenated till there is a non-object value, similar for the array, the key is concatenated on the index.

Given the way we want the output, there are two ways in which we can solve this problem, one is using stack and other is using recursion.

In this case we will go with recursion as I find it simpler to handle recursive functions.

There are three different cases which we need to handle.

  1. Check if the value of the given key is object or not.
  2. If it is not an object then add that value to the output.
  3. Else, check if the value is array or not.
  4. If it is object then recursively call the same function with value and pass the key to be used as prefix and return the output in existing result.
  5. Otherwise, iterate the value and use the array’s index along with existing key as a new key and then store it in the output.
  6. Alternatively, we can convert the array to object and then recursively call the same function as we did in step 4.

We are going to see the both approaches along with two different ways to check the type of value.

Approach 1.

const flatten = (obj, prefix) => {
  //store the result
  let output = {};
  
  //iterate the object 
  for(let k in obj){
    let val = obj[k];
    
    //get the type
    const type = Object.prototype.toString.call(val);
    
    //object
    if(type === "[object Object]"){
      //new key
      const newKey = prefix ? prefix + "." + k : k;
      const newObj = flatten(val, newKey);
      output = {...output, ...newObj};
      
    }
    //array
    else if(type === "[object Array]"){
      //iterate array
      for(let i = 0; i < val.length; i++){
        //new key
        const newKey = prefix ? prefix + "." + k + "." + i : k + "." + i;
        output = {...output, [newKey]: val[i]};
      }
    }
    // normal value
    else{
      //new key
      const newKey = prefix ? prefix + "." + k : k;
      output = {...output, [newKey]: val};
    }
  }
  
  return output;
}
Input:
const nested = {
  A: "12",
  B: 23,
  C: {
    P: 23,
    O: {
       L: 56
    },
    Q: [1, 2]
   }   
};

console.log(flatten(nested));

Output:
{
  "A": "12"
  "B": 23,
  "C.O.L": 56,
  "C.P": 23,
  "C.Q.0": 1,
  "C.Q.1": 2,
}

Approach 2.

The problem in the first approach is we have to create the new key in each condition which is not a good and it is because we are iterating the array in between, if we could convert that array to object then we can get rid of this problem.

Also we can use ES6 features to differentiate between object and array.

const flatten = (obj, prefix) => {
  //store the result
  let output = {};
  
  //iterate the object 
  for(let k in obj){
    let val = obj[k];
    
    //new key
    const newKey = prefix ? prefix + "." + k : k;
    
    //array and object both are object in js
    if(typeof val === "object"){
      // if it is array
      if(Array.isArray(val)){
        //use rest & spread together to convert 
        //array to object
        const { ...arrToObj } = val;
        const newObj = flatten(arrToObj, newKey);
        output = {...output, ...newObj};
      }
      //if it is object
      else{
        const newObj = flatten(val, newKey);
        output = {...output, ...newObj};  
      }
    }
    // normal value
    else{
      output = {...output, [newKey]: val};
    }
  }
  
  return output;
}
Input:
const nested = {
  A: "12",
  B: 23,
  C: {
    P: 23,
    O: {
       L: 56
    },
    Q: [1, 2]
   }   
};

console.log(flatten(nested));

Output:
{
  "A": "12"
  "B": 23,
  "C.O.L": 56,
  "C.P": 23,
  "C.Q.0": 1,
  "C.Q.1": 2,
}