1
\$\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 - i.e. Day 2: Corruption Checksum (below is the 1st part of Day 2; My solution for the 2nd part of Day 2 can be found here).

/*jshint esversion: 6*/
{
 'use strict';
 const INPUT =
 `6046 6349 208 276 4643 1085 1539 4986 7006 5374 252 4751 226 6757 7495 2923
1432 1538 1761 1658 104 826 806 109 939 886 1497 280 1412 127 1651 156
244 1048 133 232 226 1072 883 1045 1130 252 1038 1022 471 70 1222 957
87 172 93 73 67 192 249 239 155 23 189 106 55 174 181 116
5871 204 6466 6437 5716 232 1513 7079 6140 268 350 6264 6420 3904 272 5565
1093 838 90 1447 1224 744 1551 59 328 1575 1544 1360 71 1583 75 370
213 166 7601 6261 247 210 4809 6201 6690 6816 7776 2522 5618 580 2236 3598
92 168 96 132 196 157 116 94 253 128 60 167 192 156 76 148
187 111 141 143 45 132 140 402 134 227 342 276 449 148 170 348
1894 1298 1531 1354 1801 974 85 93 1712 130 1705 110 314 107 449 350
1662 1529 784 1704 1187 83 422 146 147 1869 1941 110 525 1293 158 1752
162 1135 3278 1149 3546 3686 182 149 119 1755 3656 2126 244 3347 157 865
2049 6396 4111 6702 251 669 1491 245 210 4314 6265 694 5131 228 6195 6090
458 448 324 235 69 79 94 78 515 68 380 64 440 508 503 452
198 216 5700 4212 2370 143 5140 190 4934 539 5054 3707 6121 5211 549 2790
3021 3407 218 1043 449 214 1594 3244 3097 286 114 223 1214 3102 257 3345`;
 const sum = (a, b) => a + b;
 const diffOfMaxMin = (diff, val) => {
 const row = val.split(/\t/);
 const max = Math.max(...row);
 const min = Math.min(...row);
 return diff.concat(max - min);
 };
 const solution = INPUT.split(/\n/)
 .reduce(diffOfMaxMin, [])
 .reduce(sum);
 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.

Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Jan 6, 2018 at 19:37
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Note: The code examples below split lines on 1+ whitespace character instead of tabs - perhaps the way it was pasted in your question converted tabs to spaces...

Removing the array of diffs

Because the overall equation for this problem would be a summation of differences, the two calls to .reduce() can be combined into one- instead of having one function to essentially push a difference into an array and then a separate function to sum those differences, just add the difference to the cumulative total. In addition to reducing the number of function calls by the number of lines, the elimination of the array of differences should drastically reduce memory consumption.

const CumulativeDiffs = (totalSoFar, val) => {
 const row = val.split(/\s+/); 
 const max = Math.max(...row);
 const min = Math.min(...row);
 return totalSoFar + max - min; //add diff to cumulative total
};
const solution = INPUT.split(/\n/)
 .reduce(CumulativeDiffs, 0);

const INPUT =
 `6046 6349 208 276 4643 1085 1539 4986 7006 5374 252 4751 226 6757 7495 2923
1432 1538 1761 1658 104 826 806 109 939 886 1497 280 1412 127 1651 156
244 1048 133 232 226 1072 883 1045 1130 252 1038 1022 471 70 1222 957
87 172 93 73 67 192 249 239 155 23 189 106 55 174 181 116
5871 204 6466 6437 5716 232 1513 7079 6140 268 350 6264 6420 3904 272 5565
1093 838 90 1447 1224 744 1551 59 328 1575 1544 1360 71 1583 75 370
213 166 7601 6261 247 210 4809 6201 6690 6816 7776 2522 5618 580 2236 3598
92 168 96 132 196 157 116 94 253 128 60 167 192 156 76 148
187 111 141 143 45 132 140 402 134 227 342 276 449 148 170 348
1894 1298 1531 1354 1801 974 85 93 1712 130 1705 110 314 107 449 350
1662 1529 784 1704 1187 83 422 146 147 1869 1941 110 525 1293 158 1752
162 1135 3278 1149 3546 3686 182 149 119 1755 3656 2126 244 3347 157 865
2049 6396 4111 6702 251 669 1491 245 210 4314 6265 694 5131 228 6195 6090
458 448 324 235 69 79 94 78 515 68 380 64 440 508 503 452
198 216 5700 4212 2370 143 5140 190 4934 539 5054 3707 6121 5211 549 2790
3021 3407 218 1043 449 214 1594 3244 3097 286 114 223 1214 3102 257 3345`;
const CumulativeDiffs = (totalSoFar, val) => {
 const row = val.split(/\s+/);
 const max = Math.max(...row);
 const min = Math.min(...row);
 return totalSoFar + max - min;
};
const solution = INPUT.split(/\n/)
 .reduce(CumulativeDiffs, 0);
console.log("solution ", solution);

Sorting

I did consider sorting the array of values in each row using Array.sort(), which works, but the values all need to be converted to integers first (e.g. using parseInt(), or just a function that returns the number added to 0), which slows things down a little. But with that change there is no need to call Math.max() and then Math.min() - simply sort the numbers, then add the last element to the cumulative total and subtract the first element.

const CumulativeDiffs = (totalSoFar, val) => { 
 //let row = val.split(/\s+/).map((num) => +num); //still slower than finding min and max
 let row = val.split(/\s+/).map((num) => parseInt(num, 10));
 row.sort((a, b) => a - b); //sort numbers in each row
 return totalSoFar + row[(row.length - 1)] - row[0]; // add difference
};

const INPUT =
 `6046 6349 208 276 4643 1085 1539 4986 7006 5374 252 4751 226 6757 7495 2923
1432 1538 1761 1658 104 826 806 109 939 886 1497 280 1412 127 1651 156
244 1048 133 232 226 1072 883 1045 1130 252 1038 1022 471 70 1222 957
87 172 93 73 67 192 249 239 155 23 189 106 55 174 181 116
5871 204 6466 6437 5716 232 1513 7079 6140 268 350 6264 6420 3904 272 5565
1093 838 90 1447 1224 744 1551 59 328 1575 1544 1360 71 1583 75 370
213 166 7601 6261 247 210 4809 6201 6690 6816 7776 2522 5618 580 2236 3598
92 168 96 132 196 157 116 94 253 128 60 167 192 156 76 148
187 111 141 143 45 132 140 402 134 227 342 276 449 148 170 348
1894 1298 1531 1354 1801 974 85 93 1712 130 1705 110 314 107 449 350
1662 1529 784 1704 1187 83 422 146 147 1869 1941 110 525 1293 158 1752
162 1135 3278 1149 3546 3686 182 149 119 1755 3656 2126 244 3347 157 865
2049 6396 4111 6702 251 669 1491 245 210 4314 6265 694 5131 228 6195 6090
458 448 324 235 69 79 94 78 515 68 380 64 440 508 503 452
198 216 5700 4212 2370 143 5140 190 4934 539 5054 3707 6121 5211 549 2790
3021 3407 218 1043 449 214 1594 3244 3097 286 114 223 1214 3102 257 3345`;
const CumulativeDiffs = (totalSoFar, val) => {
 //let row = val.split(/\s+/).map((num) => +num); //still slower than finding min and max
 let row = val.split(/\s+/).map((num) => parseInt(num, 10));
 row.sort((a, b) => a - b);
 return totalSoFar + row[(row.length - 1)] - row[0];
};
const solution = INPUT.split(/\n/)
 .reduce(CumulativeDiffs, 0);
console.log("solution ", solution);

Compare these approaches with yours in this jsPerf.

Naming Things

Like @Igor mentioned in this answer to one of your previous posts the variable names could changed to better describe the values, and functions could better describe what they do. For example - summer might be a better name than sum, since it provides a sum of values.

answered Jan 7, 2018 at 7:26
\$\endgroup\$
3
  • \$\begingroup\$ These were really great improvement suggestions. But I wonder whether the function CumulativeDiffs isn't doing "too many" things. They are (1) calculating the diffs as well as (2) sum them up. Aren't "good, clean, maintainable" functions supposed to do only one thing and one thing only? \$\endgroup\$ Commented Jan 7, 2018 at 13:48
  • 1
    \$\begingroup\$ summer might be syntactically a good description of what the function is doing, but semantically it is kind of a misnomer. summer sounds like the season summer. \$\endgroup\$ Commented Jan 7, 2018 at 16:11
  • \$\begingroup\$ You could say that function violates the Single Responsibility principle... for more discussion on that topic, see this question and it’s answers; and yeah summer sounds like a season for English speakers... one could try to find an alternative like sumFunction, sumFn, summater, etc... while it is flattering that you accepted this answer, others might be inclined to answer if you wait longer... \$\endgroup\$ Commented Jan 8, 2018 at 0:28

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.