2
\$\begingroup\$

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.

\$\endgroup\$
6
  • \$\begingroup\$ Are you allowed to hard-code the input? Isn't it supposed to take user-input? \$\endgroup\$
    – Mast
    Commented Jan 6, 2018 at 16:07
  • \$\begingroup\$ @Mast: I don't think it matters in this case. \$\endgroup\$ Commented Jan 6, 2018 at 16:27
  • \$\begingroup\$ There are some things you’re missing out on with your “no library” rule. JS doesn’t have lazy evaluation by default, but there are libs that add support. Eventually, you’ll want a good Option library too. \$\endgroup\$
    – RubberDuck
    Commented Jan 6, 2018 at 18:53
  • \$\begingroup\$ @RubberDuck: What JS FP-library would you recommend? \$\endgroup\$ Commented Jan 6, 2018 at 19:24
  • \$\begingroup\$ Nothing in particular. Just pointing out that you may be missing out on a few things. \$\endgroup\$
    – RubberDuck
    Commented Jan 6, 2018 at 19:42

2 Answers 2

2
\$\begingroup\$

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);
\$\endgroup\$
1
\$\begingroup\$

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);
\$\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.