DEV Community

Discussion on: Advent of Code 2020 Solution Megathread - Day 4: Passport Processing

Collapse
 
mustafahaddara profile image
Mustafa Haddara

Reading all of these solutions, I'm glad I opted to avoid regexes in my answers! There are some messy hacks here ("1234567890abcdef".indexOf(c) stands out) and the types should probably all have been string? instead of string, which would have made this a little more concise, but I'm still pretty happy with how it turned out.

I am annoyed they decided to put multiple K:V pairs on the same line because it made the parsing a little messier but oh well.

import { SolveFunc } from './types';

export const solve: SolveFunc = (lines: string[]) => {
  const passports: Passport[] = parse_passports(lines);
  return passports.filter((p) => is_valid(p)).length.toString();
};

type Passport = {
  byr: string;
  iyr: string;
  eyr: string;
  hgt: string;
  hcl: string;
  ecl: string;
  pid: string;
  cid: string;
};

const parse_passports = (lines: string[]): Passport[] => {
  let p: Passport = make_blank_passport();
  const result = [p];
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    if (line.trim() === '') {
      p = make_blank_passport();
      result.push(p);
    }
    line.split(' ').map((chunk) => {
      const [k, v] = chunk.split(':');
      p[k] = v;
    });
  }
  return result;
};

const make_blank_passport = (): Passport => {
  return {
    byr: null,
    iyr: null,
    eyr: null,
    hgt: null,
    hcl: null,
    ecl: null,
    pid: null,
    cid: null,
  };
};

const is_valid = (p: Passport): boolean => {
  return (
    check_int(p.byr, 1920, 2002) &&
    check_int(p.iyr, 2010, 2020) &&
    check_int(p.eyr, 2020, 2030) &&
    check_height(p.hgt) &&
    check_hair_color(p.hcl) &&
    check_eye_color(p.ecl) &&
    check_passport_id(p.pid)
  );
};

const check_int = (strval, min, max) => {
  if (!strval) return false;
  const val = parseInt(strval);
  return min <= val && val <= max;
};

const check_height = (strval) => {
  if (!strval) return false;
  if (strval.endsWith('cm')) {
    const [h] = strval.split('cm');
    return check_int(h, 150, 193);
  } else if (strval.endsWith('in')) {
    const [h] = strval.split('in');
    return check_int(h, 59, 76);
  }
  return false;
};

const check_hair_color = (strval) => {
  if (!strval) return false;
  if (strval.startsWith('#') && strval.length === 7) {
    return strval.split('').filter((c) => '1234567890abcdef'.indexOf(c) < 0).length === 1;
  }
  return false;
};

const check_eye_color = (strval) => {
  if (!strval) return false;
  const accepted = ['amb', 'blu', 'brn', 'gry', 'grn', 'hzl', 'oth'];
  return accepted.indexOf(strval) >= 0;
};

const check_passport_id = (strval) => {
  if (!strval) return false;
  if (strval.length !== 9) return false;
  strval.split('').map((c) => parseInt(c));
  return true;
};
Enter fullscreen mode Exit fullscreen mode