/**
* Class which can be iterated over and produces json for each row
* @example
const grid = Grid.create([["one", "two"], [1, 2]]);
for (const {json} of row) {
Logger.log(json); // {one: 1, two: 2}
}
* @example
// using calculatedProps
const grid = Grid.create([["one", "two"], [1, 2]], {
sum (json) {
// use header names as keys on json
return json.one + json.two;
}
});
for (const {json} of row) {
Logger.log(json); // {one: 1, two: 2, sum: 3}
}
*/
class Grid {
/**
* Created either via call to `Grid.create` or `Grid.namedValues`, but class can be returned by `Grid.module()`
* @param {Object} [np] - named parameters
* @param {Array[]} [np.data2d] - 2d array, can be set after instance creation if necessary
* @param {Object} [np.calculatedProps] - keys repesent the header, and the values should be functions that return the calculated value for that header for any object
*/
constructor ({data2d=[], calculatedProps={}}={}) {
this.data2d = data2d;
this.calculatedProps = calculatedProps;
}
*[Symbol.iterator] () {
// get headers, start at 1
const headers = this.headers;
for (let idx = 1; idx < this.data2d.length; idx++) {
const values = this.data2d[idx];
const row = new Row({headers, values, idx, calculatedProps: this.calculatedProps});
yield row;
}
}
update ({r, c, value}) {
this.data2d[r][c] = value;
}
getValues () {
return this.data2d;
}
addCalculatedProp (header, func) {
this.calculatedProps[header] = func;
}
setCalculatedProperties (props) {
this.calculatedProps = props;
}
static fromNamedValues ({obj}) {
const headers = Object.keys(obj);
const data2d = Object.values(obj).reduce(
(acc, value, idx) => {
const header = headers[idx];
acc[1].push(value.pop());
return acc;
}, [headers, []]
);
return new Grid({data2d});
}
get headers () {
return this.data2d[0];
}
set headers (headers) {
this.data2d = [headers, ...this.data2d.slice(1)];
}
}
/**
* Instances of `Row` are returned in the iterator
* @property {String[]} headers - the first row of the `data2d` passed
* @property {Any[]} values - the raw row as an array, as it appears in the spreadsheet
* @property {Number} idx - the row number, 1 indexed
*/
class Row {
/**
* Creates a row
* @param {Object} np - named parameter
* @param {String[]} np.headers - the order of headers should match that appearing in `values`
* @param {Any[]} np.values - the order of values should match that appearing in `headers`
* @param {Object} np.calculatedProps - keys are header names, returned values are the values for that row. Each value is a function, taking one parameter `json`
*/
constructor ({headers, values, idx, calculatedProps}) {
this.headers = headers;
this.values = values;
this.idx = idx;
const nativeJson = headers.reduce(
(acc, h, i) => {
acc[h] = values[i];
return acc;
}, {}
)
const extendedJson = Object.entries(calculatedProps).reduce(
(acc, [prop, func]) => {
acc[prop] = func(nativeJson);
return acc;
}, {}
);
this.json = Object.assign(nativeJson, extendedJson);
}
*[Symbol.iterator] () {
for (let h = 0; h < this.headers.length; h++) {
const header = this.headers[h];
const value = this.json[header];
const col = new Column({header, value, idx: h});
yield col;
}
}
}
class Column {
constructor ({header, value, idx}) {
this.header = header;
this.value = value;
this.idx = idx;
}
}
export {Grid};