updated by GasGit automation
This commit is contained in:
parent
d18405768c
commit
01cdb7363d
1 changed files with 842 additions and 0 deletions
842
scripts/Fiddler.js
Normal file
842
scripts/Fiddler.js
Normal file
|
@ -0,0 +1,842 @@
|
||||||
|
/**
|
||||||
|
* this is a utility for messing around with
|
||||||
|
* values obtained from setValues method
|
||||||
|
* of a spreadsheet
|
||||||
|
* @contructor Fiddler
|
||||||
|
*/
|
||||||
|
var Fiddler = function () {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var values_,
|
||||||
|
headerOb_ ,
|
||||||
|
dataOb_=[],
|
||||||
|
hasHeaders_ = true,
|
||||||
|
functions_;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* these are the default iteration functions
|
||||||
|
* for the moment the do nothing
|
||||||
|
* just here for illustration
|
||||||
|
* properties take this format
|
||||||
|
* not all are relevant for each type of function
|
||||||
|
* .name the name of the column
|
||||||
|
* .data all the data in the fiddle
|
||||||
|
* .headers the header texts
|
||||||
|
* .rowOffset the row number starting at 0
|
||||||
|
* .columnOffset the column number starting at 0
|
||||||
|
* .fiddler this object
|
||||||
|
* .values an array of values for this row or column
|
||||||
|
* .row an object with all the properties/values for the current row
|
||||||
|
*/
|
||||||
|
var defaultFunctions_ = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used to filter rows
|
||||||
|
* @param {object} row the row object
|
||||||
|
* @param {object} properties properties of this row
|
||||||
|
* @return {boolean} whether to include
|
||||||
|
*/
|
||||||
|
filterRows: function (row, properties) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used to filter columns
|
||||||
|
* @param {string} heading the heading text
|
||||||
|
* @param {object} properties properties of this column
|
||||||
|
* @return {boolean} whether to include
|
||||||
|
*/
|
||||||
|
filterColumns:function (heading , properties) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used to change objects rowwise
|
||||||
|
* @param {object} row object
|
||||||
|
* @param {object} properties properties of this row
|
||||||
|
* @return {object} modified or left as is row
|
||||||
|
*/
|
||||||
|
mapRows: function (row , properties) {
|
||||||
|
return row;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used to change values columnwise
|
||||||
|
* @param {[*]} values the values for each row of the column
|
||||||
|
* @param {object} properties properties of this column
|
||||||
|
* @return {[*]|undefined} values - modified or left as is
|
||||||
|
*/
|
||||||
|
mapColumns: function (values, properties) {
|
||||||
|
return values;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used to change values columnwise in a single column
|
||||||
|
* @param {*} value the values for this column/row
|
||||||
|
* @param {object} properties properties of this column
|
||||||
|
* @return {[*]|undefined} values - modified or left as is
|
||||||
|
*/
|
||||||
|
mapColumn: function (value, properties) {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used to change header values
|
||||||
|
* @param {string} name the name of the column
|
||||||
|
* @param {object} properties properties of this column
|
||||||
|
* @return {[*]|undefined} values - modified or left as is
|
||||||
|
*/
|
||||||
|
mapHeaders: function (name, properties) {
|
||||||
|
return name;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the indices of matching values in a column
|
||||||
|
* @param {*} value the values for this column/row
|
||||||
|
* @param {object} properties properties of this column
|
||||||
|
* @return {boolean} whether it matches
|
||||||
|
*/
|
||||||
|
selectRows: function (value , properties) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// maybe a later version we'll allow changing of default functions
|
||||||
|
functions_ = defaultFunctions_;
|
||||||
|
|
||||||
|
/// ITERATION FUNCTIONS
|
||||||
|
/**
|
||||||
|
* iterate through each row - given a specific column
|
||||||
|
* @param {string} name the column name
|
||||||
|
* @param {function} [func] optional function that shoud true or false if selected
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.selectRows = function (name, func) {
|
||||||
|
|
||||||
|
var values = self.getColumnValues(name);
|
||||||
|
var columnIndex = self.getHeaders().indexOf(name);
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
// add index if function returns true
|
||||||
|
values.forEach (function(d,i) {
|
||||||
|
if ((checkAFunc (func) || functions_.selectRows)(d, {
|
||||||
|
name:name,
|
||||||
|
data:dataOb_,
|
||||||
|
headers:headerOb_,
|
||||||
|
rowOffset:i,
|
||||||
|
columnOffset:columnIndex,
|
||||||
|
fiddler:self,
|
||||||
|
values:values,
|
||||||
|
row:dataOb_[i]
|
||||||
|
})) result.push(i);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterate through each row - nodifies the data in this fiddler instance
|
||||||
|
* @param {function} [func] optional function that shoud return a new row if changes made
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.mapRows = function (func) {
|
||||||
|
|
||||||
|
dataOb_ = dataOb_.map(function (row,rowIndex) {
|
||||||
|
var result = (checkAFunc (func) || functions_.mapRows)(row, {
|
||||||
|
name:rowIndex,
|
||||||
|
data:dataOb_,
|
||||||
|
headers:headerOb_,
|
||||||
|
rowOffset:rowIndex,
|
||||||
|
columnOffset:0,
|
||||||
|
fiddler:self,
|
||||||
|
values:self.getHeaders().map(function(k) { return row[k]; }),
|
||||||
|
row:row
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result || result.length !== row.length) {
|
||||||
|
throw new Error(
|
||||||
|
'you cant change the number of columns in a row during map items'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterate through each row - nodifies the data in this fiddler instance
|
||||||
|
* @param {function} [func] optional function that shoud return true if the row is to be kept
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.filterRows = function (func) {
|
||||||
|
|
||||||
|
dataOb_ = dataOb_.filter(function (row,rowIndex) {
|
||||||
|
return (checkAFunc (func) || functions_.filterRows)(row, {
|
||||||
|
name:rowIndex,
|
||||||
|
data:dataOb_,
|
||||||
|
headers:headerOb_,
|
||||||
|
rowOffset:rowIndex,
|
||||||
|
columnOffset:0,
|
||||||
|
fiddler:self,
|
||||||
|
values:self.getHeaders().map(function(k) { return row[k]; }),
|
||||||
|
row:row
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* iterate through each column - modifies the data in this fiddler instance
|
||||||
|
* @param {string} name the name of the column
|
||||||
|
* @param {function} [func] optional function that shoud return new column data
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.mapColumn = function (name,func) {
|
||||||
|
|
||||||
|
var values = self.getColumnValues(name);
|
||||||
|
var columnIndex = self.getHeaders().indexOf(name);
|
||||||
|
|
||||||
|
values.forEach (function (value,rowIndex) {
|
||||||
|
|
||||||
|
dataOb_[rowIndex][name] = (checkAFunc (func) || functions_.mapColumns)(value, {
|
||||||
|
name:name,
|
||||||
|
data:dataOb_,
|
||||||
|
headers:headerOb_,
|
||||||
|
rowOffset:rowIndex,
|
||||||
|
columnOffset:columnIndex,
|
||||||
|
fiddler:self,
|
||||||
|
values:values,
|
||||||
|
row:dataOb_[rowIndex]
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterate through each column - modifies the data in this fiddler instance
|
||||||
|
* @param {function} [func] optional function that shoud return new column data
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.mapColumns = function (func) {
|
||||||
|
|
||||||
|
var columnWise = columnWise_ ();
|
||||||
|
var oKeys = Object.keys(columnWise);
|
||||||
|
|
||||||
|
oKeys.forEach (function (key,columnIndex) {
|
||||||
|
// so we can check for a change
|
||||||
|
var hold = columnWise[key].slice();
|
||||||
|
var result = (checkAFunc (func) || functions_.mapColumns)(columnWise[key], {
|
||||||
|
name:key,
|
||||||
|
data:dataOb_,
|
||||||
|
headers:headerOb_,
|
||||||
|
rowOffset:0,
|
||||||
|
columnOffset:columnIndex,
|
||||||
|
fiddler:self,
|
||||||
|
values:columnWise[key]
|
||||||
|
});
|
||||||
|
|
||||||
|
// changed no of rows?
|
||||||
|
if (!result || result.length !== hold.length) {
|
||||||
|
throw new Error(
|
||||||
|
'you cant change the number of rows in a column during map items'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// need to zip through the dataOb and change to new column values
|
||||||
|
if (hold.join() !== result.join()) {
|
||||||
|
result.forEach(function(r,i) {
|
||||||
|
dataOb_[i][key] = r;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterate through each header
|
||||||
|
* @param {function} [func] optional function that shoud return new column data
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.mapHeaders = function (func) {
|
||||||
|
|
||||||
|
if (!self.hasHeaders()) {
|
||||||
|
throw new Error('this fiddler has no headers so you cant change them');
|
||||||
|
}
|
||||||
|
|
||||||
|
var columnWise = columnWise_ ();
|
||||||
|
var oKeys = Object.keys(columnWise);
|
||||||
|
var nKeys = [];
|
||||||
|
|
||||||
|
oKeys.forEach (function (key,columnIndex) {
|
||||||
|
|
||||||
|
var result = (checkAFunc (func) || functions_.mapHeaders)(key, {
|
||||||
|
name:key,
|
||||||
|
data:dataOb_,
|
||||||
|
headers:headerOb_,
|
||||||
|
rowOffset:0,
|
||||||
|
columnOffset:columnIndex,
|
||||||
|
fiddler:self,
|
||||||
|
values:columnWise[key]
|
||||||
|
});
|
||||||
|
|
||||||
|
// deleted the header
|
||||||
|
if (!result) {
|
||||||
|
throw new Error(
|
||||||
|
'header cant be blank'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
nKeys.push (result);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// check for change
|
||||||
|
if (nKeys.join() !== oKeys.join()){
|
||||||
|
headerOb_ = {};
|
||||||
|
dataOb_ = dataOb_.map(function(d) {
|
||||||
|
return oKeys.reduce(function(p,c) {
|
||||||
|
var idx = Object.keys(p).length;
|
||||||
|
headerOb_[nKeys[idx]] = idx;
|
||||||
|
p[nKeys[idx]] = d[c];
|
||||||
|
return p;
|
||||||
|
},{});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterate through each column - modifies the data in this fiddler instance
|
||||||
|
* @param {function} [func] optional function that shoud return true if the column is to be kept
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.filterColumns = function (func) {
|
||||||
|
checkAFunc (func);
|
||||||
|
|
||||||
|
var columnWise = columnWise_ ();
|
||||||
|
var oKeys = Object.keys(columnWise);
|
||||||
|
|
||||||
|
// now filter out any columns
|
||||||
|
var nKeys = oKeys.filter(function (key,columnIndex) {
|
||||||
|
var result = (checkAFunc (func) || functions_.filterColumns)(key, {
|
||||||
|
name:key,
|
||||||
|
data:dataOb_,
|
||||||
|
headers:headerOb_,
|
||||||
|
rowOffset:0,
|
||||||
|
columnOffset:columnIndex,
|
||||||
|
fiddler:self,
|
||||||
|
values:self.getColumnValues(key)
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// anything to be deleted?
|
||||||
|
if (nKeys.length !== oKeys.length) {
|
||||||
|
dataOb_ = dropColumns_ (nKeys);
|
||||||
|
headerOb_ = nKeys.reduce(function(p,c) {
|
||||||
|
p[c] = Object.keys(p).length;
|
||||||
|
return p;
|
||||||
|
} ,{});
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the values for a given column
|
||||||
|
* @param {string} columnName the given column
|
||||||
|
* @return {[*]} the column values
|
||||||
|
*/
|
||||||
|
self.getColumnValues = function(columnName) {
|
||||||
|
if (self.getHeaders().indexOf(columnName) === -1 ) {
|
||||||
|
throw new Error (columnName + ' is not a valid header name');
|
||||||
|
}
|
||||||
|
// transpose the data
|
||||||
|
return dataOb_.map(function(d) {
|
||||||
|
return d[columnName];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the values for a given row
|
||||||
|
* @param {number} rowOffset the rownumber starting at 0
|
||||||
|
* @return {[*]} the column values
|
||||||
|
*/
|
||||||
|
self.getRowValues = function (rowOffset) {
|
||||||
|
// transpose the data
|
||||||
|
return headOb_.map(function(key) {
|
||||||
|
return d[rowOffset][headOb_[key]];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* copy a column before
|
||||||
|
* @param {string} header the column name
|
||||||
|
* @param {string} [newHeader] the name of the new column - not needed if no headers
|
||||||
|
* @param {string} [insertBefore] name of the header to insert befire, undefined for end
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.copyColumn = function (header, newHeader, insertBefore) {
|
||||||
|
|
||||||
|
// the headers
|
||||||
|
var headers = self.getHeaders();
|
||||||
|
var headerPosition = headers.indexOf(header);
|
||||||
|
|
||||||
|
if (!header || headerPosition === -1) {
|
||||||
|
throw new Error ('must supply an existing header of column to move');
|
||||||
|
}
|
||||||
|
|
||||||
|
var columnOffset = insertColumn_ (newHeader, insertBefore);
|
||||||
|
|
||||||
|
// copy the data
|
||||||
|
self.mapColumns(function (values , properties) {
|
||||||
|
return properties.name === newHeader ? self.getColumnValues(header) : values;
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the range required to write the values starting at the given range
|
||||||
|
* @param {Range} range the range
|
||||||
|
* @return {Range} the range needed
|
||||||
|
*/
|
||||||
|
self.getRange = function (range) {
|
||||||
|
return range.offset (0,0,self.getNumRows() + (self.hasHeaders() ? 1 : 0) , self.getNumColumns());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* move a column before
|
||||||
|
* @param {string} header the column name
|
||||||
|
* @param {string} [insertBefore] name of the header to insert befire, undefined for end
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.moveColumn = function (header, insertBefore) {
|
||||||
|
|
||||||
|
// the headers
|
||||||
|
var headers = self.getHeaders();
|
||||||
|
var headerPosition = headers.indexOf(header);
|
||||||
|
|
||||||
|
if (!header || headerPosition === -1) {
|
||||||
|
throw new Error ('must supply an existing header of column to move');
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove from its existing place
|
||||||
|
headers.splice ( headerPosition , 1);
|
||||||
|
|
||||||
|
// the output position
|
||||||
|
var columnOffset = insertBefore ? headers.indexOf (insertBefore) : self.getNumColumns();
|
||||||
|
// check that the thing is ok to insert before
|
||||||
|
if (columnOffset < 0 || columnOffset > self.getNumColumns() ) {
|
||||||
|
throw new Error (header + ' doesnt exist to insert before');
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert the column at the requested place
|
||||||
|
headers.splice ( columnOffset , 0, header);
|
||||||
|
|
||||||
|
// adjust the positions
|
||||||
|
headerOb_ = headers.reduce(function(p,c) {
|
||||||
|
p[c] = Object.keys(p).length;
|
||||||
|
return p;
|
||||||
|
} , {});
|
||||||
|
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert a column before
|
||||||
|
* @param {string} [header] the column name - undefined if no headers
|
||||||
|
* @param {string} [insertBefore] name of the header to insert befire, undefined for end
|
||||||
|
* @return {number} the offset if the column that was inserted
|
||||||
|
*/
|
||||||
|
function insertColumn_ (header, insertBefore) {
|
||||||
|
|
||||||
|
// the headers
|
||||||
|
var headers = self.getHeaders();
|
||||||
|
|
||||||
|
// the position
|
||||||
|
var columnOffset = insertBefore ? headers.indexOf (insertBefore) : self.getNumColumns();
|
||||||
|
|
||||||
|
// check ok for header
|
||||||
|
if (!self.hasHeaders() && header) {
|
||||||
|
throw new Error ('this fiddler has no headers - you cant insert a column with a header');
|
||||||
|
}
|
||||||
|
|
||||||
|
// make one up
|
||||||
|
if (!self.hasHeaders()) {
|
||||||
|
header = columnLabelMaker_ (headers.length + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!header) {
|
||||||
|
throw new Error ('must supply a header for an inserted column');
|
||||||
|
}
|
||||||
|
if (headers.indexOf (header) !== -1 ) {
|
||||||
|
throw new Error ('you cant insert a duplicate header ' + header);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that the thing is ok to insert before
|
||||||
|
if (columnOffset < 0 || columnOffset > self.getNumColumns() ) {
|
||||||
|
throw new Error (header + ' doesnt exist to insert before');
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert the column at the requested place
|
||||||
|
headers.splice ( columnOffset , 0, header);
|
||||||
|
|
||||||
|
// adjust the positions
|
||||||
|
headerOb_ = headers.reduce(function(p,c) {
|
||||||
|
p[c] = Object.keys(p).length;
|
||||||
|
return p;
|
||||||
|
} , {});
|
||||||
|
|
||||||
|
// fill in the blanks in the data
|
||||||
|
dataOb_.forEach(function(d) {
|
||||||
|
d[header] = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
return columnOffset;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* insert a column before
|
||||||
|
* @param {string} [header] the column name - undefined if no headers
|
||||||
|
* @param {string} [insertBefore] name of the header to insert befire, undefined for end
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.insertColumn = function (header, insertBefore) {
|
||||||
|
|
||||||
|
// the headers
|
||||||
|
insertColumn_ (header, insertBefore);
|
||||||
|
return self;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert a row before
|
||||||
|
* @param {number} [rowOffset] starting at 0, undefined for end
|
||||||
|
* @param {number} [numberofRows=1] to add
|
||||||
|
* @param {[object]} [data] should be equal to number of Rows
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.insertRows = function (rowOffset,numberOfRows, data) {
|
||||||
|
if (typeof numberOfRows === typeof undefined) {
|
||||||
|
numberOfRows = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not defined insert at end
|
||||||
|
if (typeof rowOffset === typeof undefined) {
|
||||||
|
rowOffset = self.getNumRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rowOffset < 0 || rowOffset > self.getNumRows() ) {
|
||||||
|
throw new Error (rowOffset + ' is inalid row to insert before');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i =0, skeleton = [], apply = [rowOffset,0] ; i < numberOfRows ; i++) {
|
||||||
|
skeleton.push (makeEmptyObject_());
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe we have some data
|
||||||
|
if (data) {
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
data = [data];
|
||||||
|
}
|
||||||
|
if (data.length !== skeleton.length) {
|
||||||
|
throw new Error (
|
||||||
|
'number of data items ' + data.length +
|
||||||
|
' should equal number of rows ' + skeleton.length +' to insert ' );
|
||||||
|
}
|
||||||
|
// now merge with skeleton
|
||||||
|
skeleton.forEach(function(e,i) {
|
||||||
|
|
||||||
|
// override default values
|
||||||
|
Object.keys (e).forEach(function (key) {
|
||||||
|
if (data[i].hasOwnProperty(key)) {
|
||||||
|
e[key] = data[i][key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// check that no rubbish was specified
|
||||||
|
if (Object.keys(data[i]).some(function(d) {
|
||||||
|
return !e.hasOwnProperty (d);
|
||||||
|
})) {
|
||||||
|
throw new Error('unknown columns in row data to insert');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// insert the requested number of rows at the requested place
|
||||||
|
dataOb_.splice.apply (dataOb_ , apply.concat(skeleton));
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeEmptyObject_ () {
|
||||||
|
return self.getHeaders().reduce(function (p,c) {
|
||||||
|
p[c] = ''; // in spreadsheet work empty === null string
|
||||||
|
return p;
|
||||||
|
},{});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* create a column slice of values
|
||||||
|
* @return {object} the column slice
|
||||||
|
*/
|
||||||
|
function columnWise_ () {
|
||||||
|
// first transpose the data
|
||||||
|
return Object.keys(headerOb_).reduce (function (tob , key) {
|
||||||
|
tob[key] = self.getColumnValues(key);
|
||||||
|
return tob;
|
||||||
|
},{});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* will create a new dataob with columns dropped that are not in newKeys
|
||||||
|
* @param {[string]} newKeys the new headerob keys
|
||||||
|
* @return {[object]} the new dataob
|
||||||
|
*/
|
||||||
|
function dropColumns_ (newKeys) {
|
||||||
|
|
||||||
|
return dataOb_.map(function(row) {
|
||||||
|
return Object.keys(row).filter (function (key) {
|
||||||
|
return newKeys.indexOf(key) !== -1;
|
||||||
|
})
|
||||||
|
.reduce (function (p,c) {
|
||||||
|
p[c] = row[c];
|
||||||
|
return p;
|
||||||
|
},{});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the number of rows
|
||||||
|
* @return {number} the number of rows of data
|
||||||
|
*/
|
||||||
|
self.getNumRows = function () {
|
||||||
|
return dataOb_.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the number of columns
|
||||||
|
* @return {number} the number of columns of data
|
||||||
|
*/
|
||||||
|
self.getNumColumns = function () {
|
||||||
|
return Object.keys(headerOb_).length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check that a variable is a function and throw if not
|
||||||
|
* @param {function} [func] optional function to check
|
||||||
|
* @return {function} the func
|
||||||
|
*/
|
||||||
|
function checkAFunc (func) {
|
||||||
|
if (func && typeof func !== 'function') {
|
||||||
|
throw new Error('argument should be a function');
|
||||||
|
}
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make column item
|
||||||
|
* @param {object} ob the column object
|
||||||
|
* @param {string} key the key as returned from a .filter
|
||||||
|
* @param {number} idx the index as returned from a .filter
|
||||||
|
* @return {object} a columnwise item
|
||||||
|
*/
|
||||||
|
function makeColItem_ (ob,key,idx) {
|
||||||
|
return {
|
||||||
|
values:ob[key],
|
||||||
|
columnOffset:idx,
|
||||||
|
name:key
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make row item
|
||||||
|
* @param {object} row the row object as returned from a .filter
|
||||||
|
* @param {number} idx the index as returned from a .filter
|
||||||
|
* @return {object} a rowwise item
|
||||||
|
*/
|
||||||
|
function makeRowItem_ (row,idx) {
|
||||||
|
return {
|
||||||
|
values:Object.keys(headerOb_).map(function(k) { return row[k]; }),
|
||||||
|
rowOffset:idx,
|
||||||
|
data:row,
|
||||||
|
fiddler:self
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the headers
|
||||||
|
* @return {[string]} the headers
|
||||||
|
*/
|
||||||
|
self.getHeaders = function () {
|
||||||
|
return Object.keys(headerOb_);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the data
|
||||||
|
* @return {[object]} as rowwise kv pairs
|
||||||
|
*/
|
||||||
|
self.getData = function () {
|
||||||
|
return dataOb_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* replace the current data in the fiddle
|
||||||
|
* will also update the headerOb
|
||||||
|
* @param {[object]} dataOb the new dataOb
|
||||||
|
* @return {Fiddle} self
|
||||||
|
*/
|
||||||
|
self.setData = function (dataOb) {
|
||||||
|
|
||||||
|
// need to calculate new headers
|
||||||
|
headerOb_ = dataOb.reduce(function(hob,row) {
|
||||||
|
Object.keys(row).forEach(function(key) {
|
||||||
|
if (!hob.hasOwnProperty(key)) {
|
||||||
|
hob[key] = Object.keys(hob).length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return hob;
|
||||||
|
} , {});
|
||||||
|
dataOb_ = dataOb;
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialize the header ob and data on from a new values array
|
||||||
|
* @return {Fiddle} self
|
||||||
|
*/
|
||||||
|
self.init = function () {
|
||||||
|
headerOb_ = makeHeaderOb_();
|
||||||
|
dataOb_ = makeDataOb_();
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} whether a fiddle has headers
|
||||||
|
*/
|
||||||
|
self.hasHeaders = function () {
|
||||||
|
return hasHeaders_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set whether a fiddle has headers
|
||||||
|
* @param {boolean} headers whether it has
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.setHasHeaders = function (headers) {
|
||||||
|
hasHeaders_ = !!headers ;
|
||||||
|
return self.init();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set a new values array
|
||||||
|
* will also init a new dataob and header
|
||||||
|
* @param {[[]]} values as returned from a sheet
|
||||||
|
* @return {Fiddler} self
|
||||||
|
*/
|
||||||
|
self.setValues = function (values) {
|
||||||
|
values_= values;
|
||||||
|
return self.init();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the original values stored with this fiddler
|
||||||
|
* @return {[[]]} value as needed by setvalues
|
||||||
|
*/
|
||||||
|
self.getValues = function () {
|
||||||
|
return values_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the updated values derived from this fiddlers dataob
|
||||||
|
* @return {[[]]} value as needed by setvalues
|
||||||
|
*/
|
||||||
|
self.createValues = function () {
|
||||||
|
return makeValues_();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make a map with column labels to index
|
||||||
|
* if there are no headers it will use column label as property key
|
||||||
|
* @return {object} a header ob.
|
||||||
|
*/
|
||||||
|
function makeHeaderOb_ () {
|
||||||
|
|
||||||
|
return values_.length ?
|
||||||
|
((self.hasHeaders() ?
|
||||||
|
values_[0] : values_[0].map(function(d,i) {
|
||||||
|
return columnLabelMaker_ (i+1);
|
||||||
|
}))
|
||||||
|
.reduce (function (p,c) {
|
||||||
|
var key = c.toString();
|
||||||
|
if (p.hasOwnProperty(key)) {
|
||||||
|
throw 'duplicate column header ' + key;
|
||||||
|
}
|
||||||
|
p[key]=Object.keys(p).length;
|
||||||
|
return p;
|
||||||
|
},{})) : null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make a map of data
|
||||||
|
* @return {object} a data ob.
|
||||||
|
*/
|
||||||
|
function makeDataOb_ () {
|
||||||
|
|
||||||
|
// get rid of the headers if there are any
|
||||||
|
var vals = self.hasHeaders() ? values_.slice(1) : values_;
|
||||||
|
|
||||||
|
// make an array of kv pairs
|
||||||
|
return headerOb_ ?
|
||||||
|
( (vals|| []).map (function (row) {
|
||||||
|
return Object.keys(headerOb_).reduce(function (p,c) {
|
||||||
|
p[c] = row [headerOb_[c]];
|
||||||
|
return p;
|
||||||
|
},{})
|
||||||
|
})) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make values from the dataOb
|
||||||
|
* @return {object} a data ob.
|
||||||
|
*/
|
||||||
|
function makeValues_ () {
|
||||||
|
|
||||||
|
// add the headers if there are any
|
||||||
|
var vals = self.hasHeaders() ? [Object.keys(headerOb_)] : [];
|
||||||
|
|
||||||
|
// put the kv pairs back to values
|
||||||
|
dataOb_.forEach(function(row) {
|
||||||
|
vals.push (Object.keys(headerOb_).reduce(function(p,c){
|
||||||
|
p.push(row[c]);
|
||||||
|
return p;
|
||||||
|
},[]));
|
||||||
|
});
|
||||||
|
|
||||||
|
return vals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a column label for sheet address, starting at 1 = A, 27 = AA etc..
|
||||||
|
* @param {number} columnNumber the column number
|
||||||
|
* @return {string} the address label
|
||||||
|
*/
|
||||||
|
function columnLabelMaker_ (columnNumber,s) {
|
||||||
|
s = String.fromCharCode(((columnNumber-1) % 26) + 'A'.charCodeAt(0)) + ( s || '' );
|
||||||
|
return columnNumber > 26 ? columnLabelMaker_ ( Math.floor( (columnNumber-1) /26 ) , s ) : s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue