/**
* @module itertools
* @description Functional transformers for object collections using ES6
* features (e.g. computed property names)
* @see [Computed Property Names]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names}
* @see [Functional JavaScript, by Michael Fogus]{@link https://www.oreilly.com/library/view/functional-javascript/9781449360757/}
*/
/**
* Sorts the given `list` of objects according to the given `key`
* @param {Array<object>} list - An object collection
* @param {string} key - The property to sort by
* @param {boolean} [desc] - Option to sort in reverse order
* @returns {Array<object>} A sorted copy of `list`
*/
const sortBy = (list, key, desc = false) => {
const sorted = list
.map((obj, idx) => {
return { [key]: obj[`${key}`], idx }
})
.sort((a, b) => {
let first = a[`${key}`]
let second = b[`${key}`]
if (typeof a[`${key}`] === 'string' &&
typeof b[`${key}`] === 'string') {
first = a[`${key}`].toLowerCase()
second = b[`${key}`].toLowerCase()
}
if (first < second) {
return -1
}
if (first > second) {
return 1
}
return 0
})
.map(s => list[s.idx])
return desc ? sorted.reverse() : sorted
}
/**
* Collects all the values at the given `key` and totals them by applying the
* given aggregate function (`xform`); returns an ordered summary of totals
* for each `group`
* @param {Array<{string: number}>} list - An object collection
* @param {string} group - A common property to group the results under
* @param {string} key - A common property whose (numeric) value will be totalled
* @param {module:itertools~Aggregate} [xform] - An aggregating function
* @returns {Array<{string: number}>} A mapping of each item in `list`
* to its respective frequency
*/
const frequencyOf = (list, group, key, xform = (x, _, k) => x[`${k}`] += 1) => {
return sortBy(list, group).reduce((freqs, item) => {
const included =
freqs.find(it => it[`${group}`] === item[`${group}`])
if (included) {
xform(included, item, `${key}`)
} else {
freqs = freqs.concat({
[group]: item[`${group}`],
[key]: item[`${key}`] || 1
})
}
return freqs
}, [{ [group]: '', [key]: 0 }]).slice(1)
}
/**
* Takes two objects and the name of a common property, returning the sum of
* their respective values
* @typedef Aggregate
* @type {function({string: number},
{string: number},
string): number}
* @param {{string: number}} obj1 - The first object
* @param {{string: number}} obj2 - The second object
* @param {string} prop - The name of a property common to both
* @returns {number} The sum of the values of each object's common property
* @example <caption>Using an aggregating function</caption>
* const fn = (x, y, k) => x[`${k}`] + y[`${k}`]
* const h1 = {'height': 7}
* const h2 = {'height': 3}
* const totalHeight = fn(h1, h2, 'height')
* // returns 10
*/
module.exports = { sortBy, frequencyOf }