forked from brucemcpherson/cUseful
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUnnest.gs
116 lines (106 loc) · 4.25 KB
/
Unnest.gs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
var Unnest = (function (ns){
/**
* converts blowup into table
*
* @param {object[]} {blownup} the array from blowup
* @param {function} {sorter} a function to sort the headers
* @return {*[][]} the 2 dimensional array of values with the headers in the first row
*/
ns.blownupToTable = function (options) {
var blownup = options.blownup;
var sorter = options.sorter || function(mentions) {
return Object.keys(mentions).sort(function (a, b) { return a - b; });
};
// collect all the property names
var mentions = blownup.reduce(function (p, c) {
Object.keys(c).forEach(function (k, i) {
p[k] = i;
});
return p;
}, {});
// make that into a header row
var headerRow = sorter(mentions);
// now add the rows after the header
// & we dont really like undefined in sheets, so replace with null.
return [headerRow].concat(blownup.map(function(row) {
return headerRow.map(function (h) {
return typeof row[h] === typeof undefined ? null : row[h]
});
}));
};
/**
* an array of object(or an object of arrays) gets blown up into rows one row per array element
* nested arrays are handled too so an array of 5 with 10 nested array would create 50 rows and so on
* array members dont need to have the same properties, and can each contain separate nested arrays
* each flattened property is given a property name reflecting the object tree preceding, so
* {a:{b:{c:{name:'rambo'}}}}
* would be expressed as
* {a_b_c_name: 'rambo'}
* {a:{b:[{c:{name:'rambo'}}, {c:{name:'terminator'}}]}}
* would be expressed as
* [{a_b_c_name: 'rambo'},[{a_b_c_name: 'terminator'}]
* @param {object|object[]} {ob} the object to be blown up
* @param {string} [{sep}] the separator to use betwenn propertyu name sections
* @param {function} [{cloner}] a function to deep clone an obje
*/
ns.blowup = function (options) {
var ob = options.ob;
var sep = options.sep || '_';
var cloner = options.cloner || function(item) { return JSON.parse(JSON.stringify(item))};
var isObject = function (sob) {
return typeof (sob) === 'object' && !(sob instanceof Date);
};
// recursive piece
var makeRows = function (sob, rows, currentKey, cob) {
rows = rows || [];
currentKey = currentKey || '';
cob = cob || {};
// ignore undefined or null items
if (typeof sob === typeof undefined || sob === null) {
return rows;
} else if (Array.isArray(sob)) {
// going to work through an array creating 1 row for each element
// but without adding to the current key
// make deep clone of current object
sob.forEach(function(f, i) {
// make clone of what we have so far to replicate across
var clob = cloner(cob);
// the first element updates an existing row
// subsequent elements add to the number of rows
if (i) {
rows.push(clob);
} else {
rows[rows.length ? rows.length - 1 : 0] = clob;
}
// recurse for each element
makeRows(f, rows, currentKey, clob);
});
} else if (isObject(sob)) {
// deal with the non object children first so they get cloned too
Object.keys(sob).sort(function(a,b) {
return isObject(sob[a]) && isObject(sob[b]) ? 0 : (isObject(sob[b]) ? -1: 1);
}).forEach(function (k, i) {
// add to the key, but nothing to the accumulating object
makeRows(sob[k], rows, currentKey ? currentKey + sep + k : k, cob);
});
} else {
// its a natural value
if (cob.hasOwnProperty(currentKey)) {
// something has gone wrong here - show should probably be a throw
Logger.log('attempt to to overwrite property', cob, currentKey, 'row', rows.length);
} else {
cob[currentKey] = sob;
}
}
return rows;
};
// do the work - the input data should be an array of objects
if(!Array.isArray(ob)) ob = [ob];
return makeRows(ob);
};
ns.table = function (options) {
var blownup = ns.blowup(options);
return ns.blownupToTable ({ blownup: blownup, sorter: options.sorter });
};
return ns;
}) ({});