Advent of Code – Day 2 – Functional Solution

Advent of Code – Day 2 – Functional Solution

So a great blog (To Code [Hebrew]) that I follow started a social coding challenge which involve solving AOC challenges together and sharing the code. I posted my solution for the first challenge and Ynon made me think differently a bit on how to solve the challenges. He made me think more functional when solving this kind of problems. The key in functional thinking is that for each input of piece of smaller problem out of the bigger problem you get the output and you can chain those actions to make them work as a pipeline to solve the bigger problem. So I’ll try to post the way I think, and the way I tackled the 2nd day challenge.

The problem

TL;DR the problem is to get as an input a spreadsheet of numbers. The output should be the sum of the differences of the max and min value of each row. For example:

5 8 9 1
2 5 4 3
1 1 2 2 6
5 4 3

9 - 1 +
5 - 2 +
6 - 1 +
5 - 3
=
18

My solution

The input in the real AOC challenge that I got (each user gets different input, so copy my answer won’t help you :)) is:

104	240	147	246	123	175	372	71	116	230	260	118	202	270	277	292
740	755	135	205	429	822	844	90	828	115	440	805	526	91	519	373
1630	991	1471	1294	52	1566	50	1508	1367	1489	55	547	342	512	323	51
1356	178	1705	119	1609	1409	245	292	1434	694	405	1692	247	193	1482	1407
2235	3321	3647	212	1402	3711	3641	1287	2725	692	1235	3100	123	144	104	101
1306	1224	1238	186	751	734	1204	1275	366	149	1114	166	1118	239	153	943
132	1547	1564	512	2643	2376	2324	2159	1658	107	1604	145	2407	131	2073	1878
1845	91	1662	108	92	1706	1815	1797	1728	1150	1576	83	97	547	1267	261
78	558	419	435	565	107	638	173	93	580	338	52	633	256	377	73
1143	3516	4205	3523	148	401	3996	3588	300	1117	2915	1649	135	134	182	267
156	2760	1816	2442	2985	990	2598	1273	167	821	138	141	2761	2399	1330	1276
3746	3979	2989	161	4554	156	3359	173	3319	192	3707	264	762	2672	4423	2924
3098	4309	4971	5439	131	171	5544	595	154	571	4399	4294	160	6201	4329	5244
728	249	1728	305	2407	239	691	2241	2545	1543	55	2303	1020	753	193	1638
260	352	190	877	118	77	1065	1105	1085	1032	71	87	851	56	1161	667
1763	464	182	1932	1209	640	545	931	1979	197	1774	174	2074	1800	939	161

Well first thing after understanding the problem is to divide the big problem into small problems. So I start by creating a list of the tasks I should do get it work:

  1. get each line to be a value in an array so we can model it as follows
    a = [
     line1,
     line2,
     ...,
     lineN
    ]
  2. each line in the array should be an array of the numbers in the line
    a = [
     ["104", "240", "147", "246", "123", "175", "372", "71", "116", "230", "260", "118", "202", "270", "277", "292"],
     [line2],
     ...
     [lineN]
    ]
  3. sort each line so you get the max and min values in the corners
    a = [
     ["71", "104", "116", "118", "123", "147", "175", "202", "230", "240", "246", "260", "270", "277", "292", "372"],
     [line2],
     ...
     [lineN]
    ]
  4. zip each line to include only the max and min values (could be combined with step 3)
    a = [
     ["71", "372"],
     [min(line2), max(line2)],
     ...
     [min(lineN), max(lineN)]
    ]
  5. zip the difference on each line into one value, the cool feature used here is called deconstructing assignment (thank you Ynon), much more readable way to access known size array
    a = [
     301,
     max(line2) - min(line2),
     ...
     max(lineN) - min(lineN)
    ]
    
    // without deconstructing:
    //.map(line => Number(line[1]) - Number(line[0]))
    
    // with deconstructing
    //.map(([min, max]) => Number(max) - Number(min))
  6. sum the lines and return the result

Solution

function getCheckSum(input){
	return input
		.split('\n') // 1
		.map(line => line.split('\t')) // 2
		.map(line => line.sort((a,b) => a - b)) // 3
		.map(line => [line[0], line.slice(-1)]) // 4
		.map(([min, max]) => Number(max) - Number(min)) // 5
		.reduce((acc, cur) => acc + cur, 0); // 6
}

36766

P.S

There is nothing wrong with declaring or extending the javascript’s Array prototype and adding custom helper functions in order to create even more readable and declarative code.

Array.prototype.first = function(){ return this[0] }
Array.prototype.last = function(){ return this.slice(-1) }
Array.prototype.castedTo = function(type){ return this.map(x => type(x)) }
Array.prototype.sum = function(){ return this.reduce((a,c) => a+c, 0) }
function ascendingComparator(a,b){ return a - b }

function getCheckSum(input){
	return input
		.split('\n')
		.map(line => line.split('\t'))
		.map(line => line.castedTo(Number))
		.map(line => line.sort(ascendingComparator))
		.map(line => [line.first(), line.last()])
		.map(([min, max]) => max - min)
		.sum();
}

cheers,