Skip to content

Instantly share code, notes, and snippets.

@rdipardo
Created November 11, 2020 10:54
Show Gist options
  • Save rdipardo/751d4bc216fa3c85ad23a899769ac3be to your computer and use it in GitHub Desktop.
Save rdipardo/751d4bc216fa3c85ad23a899769ac3be to your computer and use it in GitHub Desktop.
itertools.js
/**
* @module itertools
* @description Functional transformers for object collections using ES6 features (e.g. computed property names)
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
*/
/**
* 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=false] - Option to sort in reverse order
* @returns {Array<Object>}
*/
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<Object.<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=(x, _, k) => x[`${k}`] += 1] - An
* aggregating function
* @returns {Array<Object.<string, number>>}
*/
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(Object.<string, number>,
Object.<string, number>,
string): number}
* @param {Object.<string, number>} - The first object
* @param {Object.<string, number>} - The second object
* @param {string} - The name of a property common to both
* @returns The sum the values of each object's common property
* @example
* 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 }
const { sortBy, frequencyOf } = require('itertools')
const blogs = [
{
'title': 'First class tests',
'author': 'Robert C. Martin',
'url': 'http://blog.cleancoder.com/uncle-bob/2017/05/05/TestDefinitions.htmll',
'likes': 10
},
{
'title': 'React patterns',
'author': 'Michael Chan',
'url': 'https://reactpatterns.com/',
'likes': 7
},
{
'title': 'Go To Statement Considered Harmful',
'author': 'Edsger W. Dijkstra',
'url': 'http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html',
'likes': 5
},
{
'title': 'TDD harms architecture',
'author': 'Robert C. Martin',
'url': 'http://blog.cleancoder.com/uncle-bob/2017/03/03/TDD-Harms-Architecture.html',
'likes': 0
},
{
'title': 'Parsing CSS for EPUB',
'author': 'Charles Petzold',
'url': 'http://ftp.charlespetzold.com/blog/2011/12/Parsing-CSS-for-EPUB.html',
'likes': 1
},
{
'title': 'Canonical string reduction',
'author': 'Edsger W. Dijkstra',
'url': 'http://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD808.html',
'likes': 12
},
{
'title': 'Type wars',
'author': 'Robert C. Martin',
'url': 'http://blog.cleancoder.com/uncle-bob/2016/05/01/TypeWars.html',
'likes': 2
}
]
//
// sortBy()
//
console.log('Unsorted:')
console.table(blogs, ['author'])
// Unsorted:
// +---------+----------------------+
// │ (index) │ author │
// +---------+----------------------+
// │ 0 │ 'Robert C. Martin' │
// │ 1 │ 'Michael Chan' │
// │ 2 │ 'Edsger W. Dijkstra' │
// │ 3 │ 'Robert C. Martin' │
// │ 4 │ 'Charles Petzold' │
// │ 5 │ 'Edsger W. Dijkstra' │
// │ 6 │ 'Robert C. Martin' │
// +---------+----------------------+
byAuthor = sortBy(blogs, 'author', true)
console.log('Alphabetically (Z-A) :')
console.table(byAuthor, ['author'])
// Alphabetically (Z-A) :
// +---------+----------------------+
// | (index) | author |
// +---------+----------------------+
// | 0 | 'Robert C. Martin' |
// | 1 | 'Robert C. Martin' |
// | 2 | 'Robert C. Martin' |
// | 3 | 'Michael Chan' |
// | 4 | 'Edsger W. Dijkstra' |
// | 5 | 'Edsger W. Dijkstra' |
// | 6 | 'Charles Petzold' |
// +---------+----------------------+
//
// frequencyOf()
//
const byBlogsPublished = frequencyOf(blogs, 'author', 'blogs')
console.log('Rankings by Blogs Published:')
console.table(sortBy(byBlogsPublished, 'blogs'))
// Rankings by Blogs Published:
// +----------------------------------------+
// | (index) | author | blogs |
// +----------------------------------------+
// | 0 | 'Charles Petzold' | 1 |
// | 1 | 'Michael Chan' | 1 |
// | 2 | 'Edsger W. Dijkstra' | 2 |
// | 3 | 'Robert C. Martin' | 3 |
// +----------------------------------------+
let max = Math.max(...byBlogsPublished.map(f => f.blogs))
let winner = byBlogsPublished.find(f => f.blogs === max)
console.log(`${winner.author} has the most blogs at ${winner.blogs}`)
// Robert C. Martin has the most blogs at 3
const countLikes = (x, y, k) => x[`${k}`] += y[`${k}`]
const byLikes = frequencyOf(blogs, 'author', 'likes', countLikes)
console.log('Rankings by Likes Received:')
console.table(sortBy(byLikes, 'likes'))
// Rankings by Likes Received:
// +----------------------------------------+
// | (index) | author | likes |
// +----------------------------------------+
// | 0 | 'Charles Petzold' | 1 |
// | 1 | 'Michael Chan' | 7 |
// | 2 | 'Robert C. Martin' | 12 |
// | 3 | 'Edsger W. Dijkstra' | 17 |
// +----------------------------------------+
max = Math.max(...byLikes.map(f => f.likes))
winner = byLikes.find(f => f.likes === max)
console.log(`${winner.author} has the most likes at ${winner.likes}`)
// Edsger W. Dijkstra has the most likes at 17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment