I wanted to practice functional programming (fp) without using any library but using vanilla JS only. So I took a problem from Advent of Code:
https://adventofcode.com/2017/day/1
The captcha requires you to review a sequence of digits (your puzzle input) and find the sum of all digits that match the next digit in the list. The list is circular, so the digit after the last digit is the first digit in the list.
My solution in fp looks like tis:
/*jshint esversion: 6*/
{
'use strict';
const INPUT =
'349977448929146532968278716\
133885529936349351737335974749973934313241\
217189424846744921337364866195152468292484\
778365444519439388328481571992241165637156\
461264314935637721127147415466357646655864\
528583493266583455245736812248292218297727\
285312788933571466387722917827967448124795\
951725785559319682853267411915587354919236\
825868441854765841246778568566125822632631\
247159164982546597613122252959473286718737\
295941826954258525597189228168328163412596\
957663223575652523358512649334715553515363\
639445727636217614899442177877855643551317\
569483314136526468116267421688576348562343\
474326989313717574541563964329934217956751\
472732296424418887765171653759652889235153\
788717734497141893111678497885194792741726\
173343784126615748851569881715324833855283\
428513585997921543318893429851685281865628\
737361171132422718633188739173554283931731\
527832237273622821699825971235256718954529\
371186871912813829493359371733238626181722\
842547419358659638773594771261888794819111\
488274537815467894373175815689314452599125\
412733533452541712525883446123866491345626\
387589153369763472912188487445487554624939\
818715439496973317355772436587221113715523\
631795845435211499442478481767935718551643\
294151437534792978799269591415976951746743\
864678547764816893146123245347291873353684\
716977389252716182433128646564422999388867\
556799965682974989656516523379618378764685\
967494334546339757225619719354595549797133\
443132925114472889393793692794872995573261\
779821964639543624174275158136375289683389\
271354362796663378845538412934763769355971\
317447726291491659899182398368622637839634\
155421954468343953693333818572383274396425\
833516399332419158924639953584543416781913\
541391644376493166838681728227987726429626\
282399922494397497448989277879965672345384\
913919494836899899553126122466947855935968\
916793462468162283493122372831824783213475\
858188273641533418756234237514469339877122\
312713256269252562939288972324237474691193\
631313638235485876716945265622451912828789\
926483146359766346185711913231257864889481\
541734836453237283662164417629577697894278\
371477895486471954183217663389214784569375\
224856514779435786485996146291884747115824\
451627917834651412911732828513234133959566\
4283';
const sum = (a, b) => a + b;
const toNumber = c => parseInt(c);
const selectTwins = (val, i, arr) => arr[(i + 1) % arr.length] === val
const parse = input => input.replace(/\s/g, '');
const parsedInput = parse(INPUT)
.split('')
.map(toNumber);
const solution = parsedInput
.filter(selectTwins)
.reduce(sum, 0);
console.log("solution ", solution);
}
Is there a better way to write it in FP with pure JavaScript, i.e. no additional FP library? Any improvement suggestions are welcomed.
2 Answers 2
As far as functional programming goes, this looks pretty nice to me, clear and readable.
One performance drawback is the splitting to a string array.
The size of the array will be similar to the input,
and it can be much smaller if you apply a filter directly on the input.
Although strings don't have a filter
method like arrays,
since the selectTwins
function would work on strings,
you could apply the filter
method of arrays on the input directly:
[].filter.apply(parsedInput, [selectTwins])
Another performance drawback is that the input is scanned twice: once to replace whitespace characters, and once more for splitting. We could use the same technique as earlier to apply a filter on the input directly instead of using a regex replacement.
Putting the above suggestions together:
const isDigit = x => /\d/.test(x);
const solution = [].filter.apply(INPUT, [isDigit])
.filter(selectTwins)
.map(toNumber)
.reduce(sum, 0);
Too many steps
Assuming the input
has been "Parsed" (white spaces removed)
It is only when you have two matching characters that you require any additional processing, parsing each char to a number before you know it to be part of a pair is redundant, also parsing both characters when you only need one is also redundant processing.
All you need is the reduction.
const sumIfPair = (a, b, i, arr) => a + (b === arr[(i + 1) % arr.length] ? Number(b) : 0);
const solution = input.split('').reduce(sumIfPair, 0)
Or if you want a little more performance you can write your own reduction function that will handle a string. We can tailor it to the situation though you may want to write a generic string reduce function if you do this sort of thing a lot. Handy for your own library.
const pairValue = (a, b) => a === b ? Number(a) : 0;
const sumPairs = (str, pairValue) => {
var i = 0;
var sum = 0;
while (i < str.length) {
sum += pairValue(str[i++], str[i % str.length]);
}
return sum;
}
const solution = sumPairs(input, pairValue);
Explore related questions
See similar questions with these tags.
Option
library too. \$\endgroup\$