|
/** |
|
* Creates a sorting function where the fields are sorted in ascending order by field names |
|
* (note: prefixing the field with `-` beforehand makes the field sort in descending order) |
|
* ex: cars.sort(createSort('Origin', '-Year', 'Name')) |
|
**/ |
|
createSort = function() { |
|
const fields = Array.from(arguments); |
|
const simpleDescending = (a,b) => a==b ? 0 : a > b ? -1 : 1; |
|
const simpleAscending = (a,b) => a==b ? 0 : a > b ? 1 : -1; |
|
const sortFunctions = fields.map(field => { |
|
if (field && field.length > 0) { |
|
if (field[0] === '-') { |
|
let newField = field.slice(1); |
|
return ((a, b) => simpleDescending(a[newField], b[newField])); //d3.descending(a[newField], b[newField])); |
|
} else { |
|
return ((a, b) => simpleAscending(a[field], b[field])); //d3.ascending(a[field], b[field])); |
|
} |
|
} |
|
}); |
|
|
|
return ((a, b) => { |
|
for (let sortFunction of sortFunctions) { |
|
let sortResult = sortFunction(a,b); |
|
if (!sortResult) continue; |
|
return sortResult; |
|
} |
|
}) |
|
}; |
|
|
|
printTable = (collection, options) => { |
|
const cleanCollection = collection || []; |
|
const cleanOptions = (options || {}); |
|
const { |
|
/** |
|
* Optional labels for columns by the property Name |
|
* ex: {Miles_per_Gallon:'Miles per Gallon'} or {0:'Miles per Gallon'} for arrays |
|
* @type {Object} |
|
**/ |
|
labels = ({}), |
|
/** |
|
* Optional array of exclusive columns to show based on the properties of each row |
|
* ex: ['Miles_per_Gallon', 'Name', 'Cylinders', etc] |
|
* @type {String[]} |
|
**/ |
|
columns = null, |
|
/** |
|
* Optional array of columns to not show based on the properties of the row |
|
* ex: ['Serial_number'] |
|
* @type {String[]} |
|
**/ |
|
columnsToExclude = [], |
|
/** |
|
* Sorting function |
|
* ex: (a,b) => b['mpg'] - a['mpg'] |
|
* or alternatively multiple column sort |
|
* ex: (a,b) => d3.ascending(a['mpg'], b['mpg']) || d3.descending(a['displacement'], b['displacement']) |
|
* @type {Function} |
|
**/ |
|
sortFn = null, |
|
/** |
|
* String style to apply to the table |
|
* @ex: 'border: 1px solid black' |
|
* @type {String} |
|
**/ |
|
tableStyle = '', |
|
/** |
|
* Styles to apply to each row |
|
* ex: 'border-bottom: 1px solid black' |
|
* @type {String} |
|
**/ |
|
rowStyle = '' |
|
} = cleanOptions; |
|
|
|
//-- get table info |
|
const firstRow = cleanCollection[0]; |
|
|
|
//-- determine the columns to share |
|
let keys = columns ? columns : Object.keys(firstRow); |
|
|
|
//-- remove any columns if specified |
|
keys = keys.filter((key) => columnsToExclude.indexOf(key) == -1); |
|
|
|
const printHeaderLabel = (key) => { |
|
if (labels.hasOwnProperty(key)) { |
|
return labels[key]; |
|
} |
|
return key; |
|
} |
|
|
|
const printValue = (val) => { |
|
let valType = typeof val; |
|
return !val ? null : (valType === 'string' || valType === 'number') ? val : JSON.stringify(val); |
|
} |
|
const valueFn = cleanOptions.valueFn || printValue; |
|
|
|
const printHeader = (collection, keys) => ` |
|
<tr style='${rowStyle}'> |
|
${keys.map(key => `<th>${printHeaderLabel(key)}</th>`).join('\n\t\t')} |
|
</tr>`; |
|
|
|
let printBody = () => (collection || []) |
|
.map((row, index) => `\n<tr style="${rowStyle}">` |
|
+ keys.map((key, keyIndex) => |
|
`<td>${valueFn(row[keys[keyIndex]])}</td>` |
|
).join('\n\t') |
|
+ '</tr>' |
|
).join('\n'); |
|
|
|
const printTable = (collection, keys) => `<table style="${tableStyle}">` |
|
+ printHeader(collection, keys) |
|
+ printBody(collection) |
|
+ '<table>'; |
|
|
|
//-- short circuit if no values are in collection |
|
if (cleanCollection.length < 1) { |
|
return `<table style='${tableStyle}'></table>`; |
|
} |
|
|
|
return printTable(sortFn ? collection.sort(sortFn) : collection, keys); |
|
} |