Summary
See Part 1
- There are 256 boxes (0-255).
- String format:
{label}{operation}{lens_focal_length}
.
- Hash the label to get the correct box number.
- If operation is
=
place a lens with that focal length into box with the label.
- If label is already in box, replace with new focal length.
- If operation is
-
take the lens with that label from the box.
- Multiple Lenses can be in a box, order matters.
- Calculate the total focal strength through all boxes.
To calculate the focal strength of a Lens you
Lens
While I could place the raw strings in boxes and decode them when needed, I
decided to make a lens class to makes it a little easier on myself. The
operation isn’t associated with the final lens, so that attribute isn’t included.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
interface lensInterface {
label: string,
focal_length: number,
}
export class Lens implements lensInterface {
label: string;
focal_length: number;
constructor(label: string, focal_length: number){
this.label = label;
this.focal_length = focal_length;
}
get [Symbol.toStringTag]() {
return `${this.label}(${this.focal_length})`;
}
}
|
Note: the Symbol.toStringTag
method isn’t important, it’s used to output
the class in a usable format other than [Object object]
Boxes
Boxes will be a 2D array filled with Lens objects, Lens[][]
, I wrote a
function to predefine them:
1
2
3
4
5
6
7
|
function init_boxes(): Lens[][]{
let boxes: Lens[][] = [];
for (let i = 0; i < 256; i++){
boxes.push([]);
};
return boxes;
}
|
Hashing
Hashing the label requires repackaging the functionality created in Part 1. This
actually was confusing me because I was hashing the whole string like in Part
1, but it actually requires the label
1
2
3
4
5
6
7
8
9
|
function hash_algorithm(label: string): number{
let sum: number = 0;
for (let i = 0; i < label.length; i++){
sum += label.charCodeAt(i);
sum = sum * 17;
sum = sum % 256;
};
return sum;
}
|
Parsing
Using the strings provided in the examples, I started with function that grabbed
chars at specific indexes
1
2
3
4
5
6
7
8
9
|
function get_label_chars(label: string): number{
return label.slice(0,2);
}
function get_operation(label: string): boolean{
return label[2] === '=';
}
function get_focal_length(label: string): number{
return parseFloat(label[label.length-1]);
}
|
This idea worked great in the example, but after not getting the correct results
using the input and doing a little debugging the labels aren’t a set 2
characters. Instead I used a regex and instead of writing 3 separate functions I
wrote one that would return them all at once.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function parse_label(label: string): any[]{
const matches = label.match(/^([A-Za-z]+)(=|-)([0-9])?/);
return [
matches[1], // label_chars
matches[2], // operation
matches[3] !== undefined ? parseFloat(matches[3]) : undefined, // focal_length
];
}
// Example use
let label_chars: string;
let operation: string;
let focal_length: number;
[label_chars, operation, focal_length] = helper.parse_label(label);
|
I think for readability it might have been better to define an interface for the
return instead of using a any[]
type. This wouldn’t have been the look I was
going for, I was aiming more towards how Python return multiple variables. The
trade off is these 3 variables should be constants instead of mutable.
Sorting
This is the meat of the challenge: do the encoded operations and sort lenses
into boxes. Generally self explanatory: push new lenses into the correct array
and slice the array to get a lens out.
I did write a helper function that returns the lens object if it’s in the box
already. Originally this function return a false
and was typed boolean
, but
the transcoder yelled at me because true
doesn’t have a attribute
focal_length
; I guess that’s the trade off of using Typescript, but returning
undefined is a little more fitting in this situation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
export function get_lens_in_box(box: Lens[], label_char: string): Lens|undefined{
const filtered_box: Lens[] = box.filter((ele) => ele.label == label_char);
if (filtered_box.length){
return filtered_box[0];
}
return undefined;
}
data.forEach((label, counter) => {
let label_chars: string;
let operation: string;
let focal_length: number;
[label_chars, operation, focal_length] = helper.parse_label(label);
const box_number: number = helper.hash_algorithm(label_chars);
// get Label in box from label_chars
let label_obj: helper.Lens|boolean = helper.get_lens_in_box(
boxes[box_number], label_chars);
if (operation == '='){
// adding / swapping
if (label_obj === undefined){
// isn't in box, add it
boxes[box_number].push(new helper.Lens(label_chars, focal_length));
}else{
// in box, update focal length
label_obj.focal_length = focal_length;
}
}else if (label_obj !== undefined){
// removing
boxes[box_number].splice(boxes[box_number].indexOf(label_obj), 1);
}
});
|
Focal Strength
To get the final answer it takes a nested loop and keeping track of the indexes:
1
2
3
4
5
6
7
8
|
let sum: number = 0;
boxes.forEach((box, box_position) => {
box.forEach((lens, lens_position) => {
sum += (box_position + 1) * (lens_position + 1) * lens.focal_length;
});
});
console.log(`Sum => ${sum}`);
|
and Fin.