Day 15 Part 2

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.

comments powered by Disqus