5
\$\begingroup\$

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.

\$\endgroup\$
7
  • \$\begingroup\$ Just curious, is there a reason for using 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\$ Jan 11, 2018 at 21:13
  • 2
    \$\begingroup\$ There's no purpose to a lot of the things you're doing here. This reminds me of some sarcastic code I wrote recently. No offense but you're trying way too hard. There's no reason this task requires 6 functions. The spirit of functions is about making code reusable - you're completely missing that point. You functions are only used once each. This is a one-liner, even in "functional" js. 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\$ Jan 11, 2018 at 21:34
  • \$\begingroup\$ techfindings.one/archives/2652 \$\endgroup\$ Jan 11, 2018 at 22:19
  • \$\begingroup\$ Thanks for sharing your links. I really appreciate and will read them through and hopefully won't repeat my mistakes. Thanks for sharing. \$\endgroup\$ Jan 12, 2018 at 7:34
  • 1
    \$\begingroup\$ @Iwrestledabearonce. your code is technically a one liner but not really declarative and easy to understand. I think it is better to break them down into smaller functions. Also, like I said in a previous question of mine, I don't agree on all the points. What I agree on in the article is that FP should be easy to read and understand. \$\endgroup\$ Jan 12, 2018 at 16:32

1 Answer 1

2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.