I wanted to practice functional programming (FP) without using any library but using vanilla JavaScript only. So I took a problem from Advent of Code (the 2nd part of Day 4)
You can only access the 2nd part if you type in the solution for the 1st part: 466
or look at my solution for part 1
--- Part Two --- For added security, yet another system policy has been put in place. Now, a valid passphrase must contain no two words that are anagrams of each other - that is, a passphrase is invalid if any word's letters can be rearranged to form any other word in the passphrase.
For example:
- abcde fghij is a valid passphrase.
- abcde xyz ecdab is not valid - the letters from the third word can be rearranged to form the first word.
- a ab abc abd abf abj is a valid passphrase, because all letters need to be used when forming another word.
- iiii oiii ooii oooi oooo is valid.
- oiii ioii iioi iiio is not valid - any of these words can be rearranged to form any other word. Under this new system policy, how many passphrases are valid?
My solution in FP:
const INPUT =
`pphsv ojtou brvhsj cer ntfhlra udeh ccgtyzc zoyzmh jum lugbnk
spjb xkkak anuvk ejoklh nyerw bsjp zxuq vcwitnd xxtjmjg zfgq xkpf
...
juo pmiyoh xxk myphio ogfyf dovlmwm moevao qqxidn`;
const get = input => input.split('\n');
const countDuplicate = words => words.reduce((acc, word) => {
return Object.assign(acc, {[word]: (acc[word] || 0) + 1});
}, {});
const onlyUniqueWords = phrases => {
const words = phrases.split(' ');
const duplicateWords = countDuplicate(words);
return !Object.values(duplicateWords).some(w => w > 1);
};
const sortWords = words => words.map(word => word.split('').sort().join(''));
const noAnagrams = phrases => {
const words = phrases.split(' ');
const sortedWords = sortWords(words).sort().join(' ');
return onlyUniqueWords(sortedWords);
};
const phrasesWithNoAnagrams = get(INPUT)
.filter(noAnagrams);
console.log("solution ", phrasesWithNoAnagrams.length);
Is there a better way to write it in FP with pure JavaScript, i.e. no additional FP library? Any other improvement suggestions are welcomed.
1 Answer 1
I don't see much that I would change about this code. I recently discovered that the spread syntax can be used to split a string into an array so that could be used to eliminate calling the split()
function.
Instead of
const sortWords = words => words.map(word => word.split('').sort().join(''));
use the spread syntax:
const sortWords = words => words.map(word => [...word].sort().join(''));
In this reduction:
const countDuplicate = words => words.reduce((acc, word) => { return Object.assign(acc, {[word]: (acc[word] || 0) + 1}); }, {});
I presume that the only reason you used brackets with a return statement is because it would be too long for a single line. You could avoid those by pulling out the arrow function to a separate line:
const countWord = (acc, word) => Object.assign(acc, {[word]: (acc[word] || 0) + 1});
const countDuplicate = words => words.reduce(countWord, {});
I know that is only one line shorter but at least there is no need for the brackets and return statement.
Explore related questions
See similar questions with these tags.
Object.assign
when you're only adding a single property to it? Please tell me you're not doing that just because it's "functional?" \$\endgroup\$const hasAnagram = input=>input.split(' ').map(i=>i.split('').sort().join('')).sort().reduce((ac,cv,id,ar)=>ac || (id && ar[id-1] === cv), false);
\$\endgroup\$