Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Forked from airportyh/example.js
Last active January 4, 2018 22:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfkaye/d715d9e99d4ef9298cac4176038b1c49 to your computer and use it in GitHub Desktop.
Save dfkaye/d715d9e99d4ef9298cac4176038b1c49 to your computer and use it in GitHub Desktop.
refactoring challenge from @airporty => https://twitter.com/airportyh/status/931540087602581504
// 17 November 2017
// original https://gist.github.com/airportyh/203f7c2d8a8b29af44218b79596af32f
// from @airporty https://twitter.com/airportyh/status/931540087602581504
// The initial refactoring breaks `main()` steps to smaller functions, but adds
// whitespace.
// 21 November 2017 - added discrete unit tests for each function, vastly more
// robust, heavily renamed, heavier on comments.
// 4 Jan 2018 - more method renaming.
function getPluralFor(number) {
// simplified by unit test - accept only number or coercible param
return Number(number) === 1 ? '' : 's'
}
function getNameFor(author) {
// fixed by unit test with trim()
var name = (author.name || '').trim()
return name
|| [author.firstName, author.lastName]
.join(' ')
.replace(/'/g, '')
.trim()
|| '<Unknown author>'
}
function getArticlesBy(author) {
var articles = author.articles;
return Array.isArray(articles) ? articles : []
}
function getTitleFor(article) {
// fixed by unit test with article and title.trim()
var title = article.title
return typeof title == 'string' && title.trim() || '<Untitled>'
}
function useLettersAndDigits(word) {
return word.replace(/[^\d^\w]/g, '')
}
function isNonEmpty(word) {
// filter added by unit test
var length = typeof word == 'string' ? word.trim().length : 0
return !!length
}
function getWordcountFor(article) {
// type check added by unit tests
contents = article && typeof article.contents == 'string' ? article.contents.trim() : ''
// filter added by unit test
var words = contents
.split(/[\s]+/)
.map(useLettersAndDigits)
.filter(isNonEmpty);
var set = new Set(words)
// return check fixed by unit tests
// if words.length is 0, return 0
// else return size of unique set
return words.length && set.size
}
function forEachArticle(article) {
// fixed by unit testing
var title = getTitleFor(article)
var wordCount = getWordcountFor(article)
return { title, wordCount }
}
function forEachTitle(article) {
// title fixed by unit testing
var title = getTitleFor(article)
var wordCount = article.wordCount
var plural = getPluralFor(wordCount)
return ` Article "${title}" has ${wordCount} unique word${plural}.`
}
function forEachAuthor(author) {
// default check added by unit tests
if (author == null) {
return undefined
}
var name = getNameFor(author)
var articles = getArticlesBy(author)
var articleCount = articles.length
var plural = getPluralFor(articleCount)
var aboutAuthor = `Author "${name}" has published ${articleCount} article${plural}.`
var aboutTitles = articles
.map(forEachArticle)
.map(forEachTitle)
return {
author: aboutAuthor,
titles: aboutTitles
}
}
/* entry point */
function main(authors) {
if (!Array.isArray(authors) || !authors.length) {
return
}
authors.map(forEachAuthor).map(about => {
console.warn(about.author)
about.titles.map(function(title) {
console.log(title)
})
})
}
/* test it out */
console.info("/* main api test */")
var fixture = [
{ name: 'test'},
{ firstName: 'firsty'},
{ firstName: 'firsty', lastName: 'lasty'},
{ name: 'namey', firstName: 'firsty', lastName: 'lasty'},
{ articles: []},
{ articles: [{ title: 'short Title'}]},
{ articles: [{ title: '', contents: "not a very long article"}]},
{ name: 'some author', articles: [{ title: 'some title', contents: "some contents containing 5 words."}]},
{
firstName: 'Author', lastName: 'McAuthor',
articles: [
{
title: 'Interesting Book with Words',
contents: "I found this book so awesome I can't hardly read it anymore, full stop."
},
{
title: 'Rex Tillerson\'s Guide to the Department of State',
contents: "I found this book really awesome and I couldn't put it down after 10.5 minutes."
},
{
title: 'Lance Shmendrickson',
contents: "Nothing but numbers like, 8, 8, and 8."
}
]
}
]
/* call it */
main(fixture)
// Should see =>
/*
Author "test" has published 0 articles.
Author "firsty" has published 0 articles.
Author "firsty lasty" has published 0 articles.
Author "namey" has published 0 articles.
Author "<Unknown author>" has published 0 articles.
Author "<Unknown author>" has published 1 article.
Article "short Title" has 0 unique words.
Author "<Unknown author>" has published 1 article.
Article "<Untitled>" has 5 unique words.
Author "some author" has published 1 article.
Article "some title" has 5 unique words.
Author "Author McAuthor" has published 3 articles.
Article "Interesting Book with Words" has 13 unique words.
Article "Rex Tillerson's Guide to the Department of State" has 14 unique words.
Article "Lance Shmendrickson" has 6 unique words.
*/
/* unit tests */
console.log("\n")
console.info("/* unit tests */")
console.info("assertions should print test title followed by")
console.info("%c true", 'color: blue')
console.log("expect getPluralFor('3') to return 's'",
getPluralFor('3') === 's')
console.log("expect getPluralFor(1) to return ''",
getPluralFor(1) === '')
console.log("expect getPluralFor(-2) to return 's'",
getPluralFor(-2) === 's') // pathological test
console.log("expect getNameFor({ name: 'james' }) to return 'james'",
getNameFor({ name: 'james' }) === 'james')
console.log("expect getNameFor({ name: 'james', lastName: 'last' }) to return only 'james'",
getNameFor({ name: 'james', lastName: 'last' }) === 'james')
console.log("expect getNameFor({ firstName: 'james', lastName: 'last' }) to return 'james last'",
getNameFor({ firstName: 'james', lastName: 'last' }) === 'james last')
console.log("expect getNameFor({ name: ' ', firstName: ' ', lastName: ' ' }) to return '<Unknown author>'",
getNameFor({ name: ' ', firstName: ' ', lastName: ' ' }) === '<Unknown author>')
console.log("expect getNameFor({ }) to return empty array",
getNameFor({ }).length === 0)
console.log("expect getNameFor({ articles: { one: 'one article' } }) to return empty array",
getNameFor({ articles: { one: 'one article' } }).length === 0)
console.log("expect getNameFor({ articles: [{ title: 'one article' }] }) to return array of one item",
getNameFor({ articles: [{ title: 'one article' } ] })[0].title === 'one article')
console.log("expect getTitleFor({ title: 'one article' }) to return 'one article'",
getTitleFor({ title: 'one article' }) === 'one article')
console.log("expect getTitleFor({ title: ' ' }) to return '<Untitled>'",
getTitleFor({ title: ' ' }) === '<Untitled>')
console.log("expect useLettersAndDigits('123.456') to return '123456'",
useLettersAndDigits('123.456') === '123456')
console.log("expect useLettersAndDigits('comma,') to return 'comma'",
useLettersAndDigits('comma,') === 'comma')
console.log("expect useLettersAndDigits('1for2') to return '1for2'",
useLettersAndDigits('1for2') === '1for2')
console.log("expect useLettersAndDigits('self-regard') to return 'selfregard'",
useLettersAndDigits('self-regard') === 'selfregard')
console.log("expect useLettersAndDigits('') to return ''",
useLettersAndDigits('') === '')
console.log("expect isNonEmpty(' ') to return false",
isNonEmpty(' ') === false)
console.log("expect isNonEmpty(' 5 ') to return true",
isNonEmpty(' 5 ') === true)
console.log("expect getWordcountFor({ contents: 'one one' }) to return 1",
getWordcountFor({ contents: 'one one' }) === 1)
console.log("expect getWordcountFor({ contents: ' ' }) to return 0",
getWordcountFor({ contents: ' ' }) === 0)
console.log("expect getWordcountFor({ contents: ' h red 99 ' }) to return 3",
getWordcountFor({ contents: ' h red 99 ' }) === 3)
console.log("expect forEachArticle({ title: 'test' }).title to return 'test'",
forEachArticle({ title: 'test' }).title === 'test')
console.log("expect forEachArticle({ contents: 'testing one two three and three only ' }).wordCount to return 6",
forEachArticle({ contents: 'testing one two three and three only ' }).wordCount === 6)
console.log("expect forEachTitle({ title: 'test', wordCount: 3 }) to return \" Article \"test\" has 3 unique words.\"",
forEachTitle({ title: 'test', wordCount: 3 }) === " Article \"test\" has 3 unique words.")
console.log("expect forEachTitle({ wordCount: 1 }) to return \" Article \"<Untitled>\" has 1 unique word.\"",
forEachTitle({ wordCount: 1 }) === " Article \"<Untitled>\" has 1 unique word.")
console.log("expect forEachAuthor() to return undefined",
forEachAuthor() === undefined)
console.log("expect forEachAuthor({ articles: [1,2,3] }).author to return \"Author \"<Unknown author>\" has published 3 articles.\"",
forEachAuthor({ articles: [1,2,3] }).author === "Author \"<Unknown author>\" has published 3 articles.")
console.log("expect forEachAuthor({ firstName: 'Ted', lastName: 'Johanson', articles: [8] }).author to return \"Author \"Ted Johanson\" has published 1 article.\"",
forEachAuthor({ firstName: 'Ted', lastName: 'Johanson', articles: [8] }).author === "Author \"Ted Johanson\" has published 1 article.")
// original from @airporty
// gist https://gist.github.com/airportyh/203f7c2d8a8b29af44218b79596af32f
// tweet https://twitter.com/airportyh/status/931540087602581504
function main(authors) {
authors.forEach(function(author) {
let name, articles;
if (author) {
if (author.name) {
name = author.name;
} else if (author.firstName) {
if (author.lastName) {
name = author.firstName + ' ' + author.lastName;
} else {
name = author.firstName;
}
}
}
if (!name) {
name = '<Unknown author>';
}
if (author) {
articles = author.articles;
}
if (!articles) {
articles = [];
}
let articleCount = articles.length;
let maybeS = articleCount > 1 || articleCount === 0 ? 's' : '';
console.log(`${name} has published ${articles.length} article${maybeS}.`);
articles.forEach(function(article) {
let title, wordCount;
if (article) {
title = article.title;
}
if (!title) {
title = '<Untitled>';
}
if (article) {
if (article.contents) {
let words = article.contents
.split(/[^A-Za-z']+/)
.filter(word => !!word);
wordCount = words.length;
}
}
if (!wordCount) {
wordCount = 0;
}
console.log(` Article ${title} has ${wordCount} words.`);
});
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment