In Flattening an object with dot syntax I showed how to take an object of more than 1 level deep and flatten it so it could be represented in a two-dimensional object like a spreadsheet. Now here’s the opposite, unflattening an object created that way. I also provide a library you can use for flattening and unflattening, as well as processing objects to and from spreadsheets

Here’s the library reference

MqxKdBrlw18FDd-X5zQLd7yz3TLx7pV4j
Consider this object,
`{a:1,b:2,"c.d":3,"c.e.f":25,"g.0":1,"g.1":2,"g.2":3})`

which has been flattened from this original object

`{a:1,b:2,c:{d:3,e:{f:25}},g:[1,2,3]}`

Note how an array is handled using the index number as a part of the flattened property

Here’s how to use.
flatten an object
```var f = new cFlatten.Flattener();
Logger.log( f.flatten({a:1,b:2,c:{d:3,e:{f:25}},g:[1,2,3]}));```

result

`{g.2=3.0, b=2.0, g.1=2.0, c.d=3.0, g.0=1.0, a=1.0, c.e.f=25.0}`

unflatten an object

```var f = new cFlatten.Flattener();
Logger.log( f.unFlatten(  {a:1,b:2,"c.d":3,"c.e.f":25,"g.0":1,"g.1":2,"g.2":3}));```

result

```{g=[1.0, 2.0, 3.0], b=2.0, c={d=3.0, e={f=25.0}}, a=1.0}
```

create spreadsheet representation of values from an object

``` var f = new cFlatten.Flattener();
var obs = [{a:1,b:2,c:{d:3,e:{f:25}},g:[1,2]}, {a:3,b:2,c:{d:2,e:{f:5}},g:[11,12,13]}];
values = f.getValues ( obs);
Logger.log(JSON.stringify(values));```

result

` [["a","b","c.d","c.e.f","g.0","g.1","g.2"],[1,2,3,25,1,2,""],[3,2,2,5,11,12,13]]`

create an unflattened object from spreadsheet values

```var f = new cFlatten.Flattener();
var v = [["a","b","c.d","c.e.f","g.0","g.1","g.2"],[1,2,3,25,1,2,""],[3,2,2,5,11,12,13]];
Logger.log(JSON.stringify(f.getObjectsFromValues(v)));```

result

` [{"a":1,"b":2,"c":{"d":3,"e":{"f":25}},"g":[1,2,""]},{"a":3,"b":2,"c":{"d":2,"e":{"f":5}},"g":[11,12,13]}]`

Putting it all together, start with an object, write it to a sheet

```  var f = new cFlatten.Flattener();
var obs = [{a:1,b:2,c:{d:3,e:{f:25}},g:[1,2]}, {a:3,b:2,c:{d:2,e:{f:5}},g:[11,12,13]}];
var sheet = ss.getSheetByName("flatten");
var values = f.getValues(obs);
sheet.getRange(1,1,values.length,values[0].length).setValues(values);```

result

 a b c.d c.e.f g.0 g.1 g.2 1 2 3 25 1 2 3 2 2 5 11 12 13
Page Content

#### create an unflattened object from a sheet

```var ss = SpreadsheetApp.openById("12pTwh5Wzg0W4ZnGBiUI3yZY8QFoNI8NNx_oCPynjGYY");
var f = new cFlatten.Flattener();
var sheet = ss.getSheetByName("flatten");
var obs = f.getObjectsFromValues(sheet.getDataRange().getValues());
Logger.log(JSON.stringify(obs));```

result

`  [{"a":1,"b":2,"c":{"d":3,"e":{"f":25}},"g":[1,2,""]},{"a":3,"b":2,"c":{"d":2,"e":{"f":5}},"g":[11,12,13]}]`

#### The code

Here’s the library reference
MqxKdBrlw18FDd-X5zQLd7yz3TLx7pV4j
```/**
* @param {string} optObKeep if specified,objects of this key wont be flattened
* @return {Flattener} self
*/
function getLibraryInfo () {

return {
info: {
name:'cFlatten',
version:'2.2.1',
key:'MqxKdBrlw18FDd-X5zQLd7yz3TLx7pV4j',
},
dependencies:[
]
};
}
var Flattener = function(optObKeep) {
var self = this;
self.obKeep = optObKeep || null;
self.keepDates = false;
self.setKeepDates = function (keep) {
self.keepDates = keep;
return self;
};

return self;
};

/** get an array of objects from sheetvalues and unflatten them
* @parameter {Array.object} values a 2 dim array of values return by spreadsheet.getValues()
* @return {object} an unflatten object
**/
Flattener.prototype.getObjectsFromValues = function (values) {
var self = this;
var obs = [];
for (var i=1 ; i < values.length ; i++){
var k = 0;
obs.push(self.unFlatten(values[i].reduce (function (p,c) {
p[values[0][k++]] = c;
return p;
} , {})));
}
return obs;

};

/** get values from an array of objects by flattening and sorting all the keys found
* @parameter {Array.object} obs an array of objects
* @return {Array.object} a two dim array of values
**/
Flattener.prototype.getValues = function(obs) {
var self = this;

return [headingValues].concat(obs.map ( function (row) {
var v =[];
for (var i=0;i<width;i++)v.push('');
var o = self.flatten(row);
Object.keys(o).forEach( function (k) {
});
return v;
}));

};

/** get headings from an array of objects by flattening and sorting all the keys found
* @parameter {Array.object} obs an array of objects
* @return {object} a flattened object with a property for each key and its position
**/
var self = this;
obs.forEach ( function (row) {
if (!p.hasOwnProperty(c)) {
p = 0;
}
return p;
});
// sort the keys
return Object.keys(headings).sort ( function (a,b) {
return a > b ? 1 : ( a===b ? 0 : -1);
})
.reduce(function (p,c) {
p = n++;
return p;
},{});
};

/** unFlatten an ob
* creates this {a:1,b:2,c:{d:3,e:{f:25}},g:[1,2,3]}
* from this {a:1,b:2,"c.d":3,"c.e.f":25,"g.0":1,"g.1":2,"g.2":3}
* @parameter {object} ob the object to be unflattened
* @return {object} the unflattened object
**/
Flattener.prototype.unFlatten = function (ob) {
var self = this;
return Object.keys(ob).reduce(function (p,c) {
var pk=p, keys = c.split(".");
for (var i=0; i < keys.length-1 ;i++) {
if (!pk.hasOwnProperty(keys[i])) {
pk[keys[i]] = self.isNumber(keys[i+1]) ? [] : {};
}
pk = pk[keys[i]];
}
var k = keys[keys.length-1];
pk[k] = ob;
return p;
},Array.isArray(ob) ? [] : {});

};

/** flatten an ob
* turns this {a:1,b:2,c:{d:3,e:{f:25}},g:[1,2,3]}
* into this {a:1,b:2,"c.d":3,"c.e.f":25,"g.0":1,"g.1":2,"g.2":3}
* @parameter {object} ob the object to be flattened
* @return {object} the flattened object
**/
Flattener.prototype.flatten = function(ob) {
var self = this;
return  self.objectDot (ob).reduce(function(p,c){
p = c.value;
return p;
},{});
};

Flattener.prototype.objectSplitKeys  = function (ob,obArray,keyArray) {
obArray = obArray || [];
var self = this;
//turns this {a:1,b:2,c:{d:3,e:{f:25}}}
// into this, so that the keys can be joined to make dot syntax
//[{key:[a], value:1},{key:[b], value:2} , {key:, value:3}, {key:, value:25}]

if (self.isObject(ob)) {

Object.keys(ob).forEach ( function (k) {
var ka = keyArray ? keyArray.slice(0) : [];
ka.push(k);

if(self.isObject(ob[k])  && (!self.obKeep || !ob[k][self.obKeep]) && ( !self.keepDates || !self.isDateObject(ob[k]))) {
self.objectSplitKeys (ob[k],obArray,ka);
}
else {
obArray.push ( {key:ka, value:ob[k]} );
}

});
}
else {
obArray.push(ob);
}

return obArray;
};

Flattener.prototype.objectDot = function (ob) {
var self = this;
return self.objectSplitKeys (ob).map ( function (o) {
return {key:o.key.join("."), value:o.value};
});
};

Flattener.prototype.isObject  = function (obj) {
return obj === Object(obj);
};

Flattener.prototype.isNumber = function (s) {
return !isNaN(parseInt(s,10)) ;
};

Flattener.prototype.isDateObject = function (ob) {
return this.isObject(ob) && ob.constructor && ob.constructor.name === "Date";
};

/** get headings from an array of objects by flattening and sorting all the keys found
* @parameter {Array.object} obs an array of objects
* @return {Array.object} an array of heading values
**/