Similar to the Advent of Code, the Hanukkah of Data (archive) was released in December 2022.

I don’t have time to do that during the holidays, so January 2023 it is for me.

1. Plan of attack

There is a table of customers (phone and name), and we need to find the person’s whose phone number corresponds to their name according to a T9 keyboard.

This seems like an American thing only. Confirmed by they phone number format I can see in the data.

  1. Read only the phone and name from the data

  2. Write a function that converts a name to a phone number it

  3. Map all names to they computed phone number

  4. Find the one whose phone number match.

2. Implementation

Reading data from an SQLite db is much easier than trying to parse it from csv, so I’m going to use just that.

I’m unsure how to write the name to phone number function in SQL, though.

I’m going to read the db from Java (17), and handle the rest in code:

package org.example;

import org.sqlite.SQLiteDataSource;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;

public class Puzzle1 {
  public static void main(String[] args) {
    final var startTime = Instant.now();
    Puzzle1Ter.run();
    final var endTime = Instant.now();
    System.out.println("Duration: " + Duration.between(startTime, endTime).toString());
  }

  private static void run() {
    final var dbUrl = "jdbc:sqlite:/path/to/noahs.sqlite";

    final var dataSource = new SQLiteDataSource();
    dataSource.setUrl(dbUrl);

    final String query = "select name, phone from customers";
    try (final Connection conn = dataSource.getConnection();
         final Statement s = conn.createStatement();
         final ResultSet rs = s.executeQuery(query)) {

      while (rs.next()) {
        final var name = rs.getString("name");
        final var phone = rs.getString("phone");
        final var names = Arrays.asList(name.split(" "));
        // assumption: if only 1 name, that person has no family name, filter it out.
        if (names.size() > 1) {
          // names can have more than 1 element, like James Travis Jr.
          // assumption: last element is always the family name
          // assumption: given name / surname order is always the same
          final var familyName = names.get(names.size() - 1);
          final var computedPhone = Puzzle1.toPhoneNumber(familyName);
          if (computedPhone.equals(phone)) {
            System.out.println("Match found: " + phone);
          }
        }
      }
    } catch (final SQLException e) {
      e.printStackTrace(System.err);
    }
  }

  private static String toPhoneNumber(final String name) {
    final var output = new StringBuilder();
    for (final var c : name.toLowerCase().toCharArray()) {
      // phone number format seems to be xxx-xxx-xxxx
      // so add dashes if there are enough characters in this name
      // improvement: filter out names that would be too short in the first place
      if (output.length() == 3 || output.length() == 7) {
        output.append("-");
      }
      switch (c) {
        case 'a', 'b', 'c' -> output.append("2");
        case 'd', 'e', 'f' -> output.append("3");
        case 'g', 'h', 'i' -> output.append("4");
        case 'j', 'k', 'l' -> output.append("5");
        case 'm', 'n', 'o' -> output.append("6");
        case 'p', 'q', 'r', 's' -> output.append("7");
        case 't', 'u', 'v' -> output.append("8");
        case 'w', 'x', 'y', 'z' -> output.append("9");
        default -> output.append("$");
      }
    }
    return output.toString();
  }
}

3. Result

A single match is found in 140 ms, and it’s the right one.

Time to solve: 54 minutes.