Home > Reflections | ⏮️ ⏭️

2024-06-07

🐈 I walked 2 of our 3 cats this morning.
🧭 Butterscotch had quite the adventure, exploring the front yard for the first time!
🏃🏻 It’s been about a week, but I finally got out for a run on this beautiful, sunny Friday morning.
⌚ Of course, I noticed the absence of my Fitbit about halfway through 🙄.
🤷🏻 C’est la vie, I suppose.
🐍 I also noticed a snake on the running trail for the first time!
🏎️ It had a racing stripe and was rapidly side winding its way across the pavement.
🦵🏻Recently, my shins hurt during and after a run.
🔍 I may need to do some research on shin splints.
🚲 Maybe I should get a bike for some low impact cardio.
🐟 I had can of salmon for lunch.
🚿 Then a cold shower
☕ And a cup of coffee before my…

🏋🏻 Mock Interview

🔜 The real thing is scheduled for the 17th, but my prospective employer offers a mock interview before the real thing.
😁 I felt great physically, mentally, and emotionally.
🔮 I’ll definitely aim for a similar pre-interview schedule for the real thing.
💁🏻 My interviewer was friendly and helpful.
🔬 He explained that we’d work on a single problem to give him time to add commentary and tips throughout the process.

The warm up behavioral question

🧊 This isn’t evaluated, is intended as an ice breaker, and I should aim to keep my response brief: 1-2 minutes.

Tell me about your favorite project you’ve worked on.

📜 For some reason, the first project that came to mind was about 10 years old. After he asked, I mentioned another, more recent side project.

📝 I should write out and rehearse answers to some example questions like this.
⚡ It’s not a big deal, but having stories ready can save time and reduce unnecessary stress or awkwardness from improv.

The Problem

Write a function that returns the English language representation of a signed, 32-bit integer.

📑 This isn’t a particularly challenging problem, algorithmically, but it’s kind of a messy problem, thus can be good practice for managing corner cases and code complexity.

⏮️ I found the problem on LeetCode and (using feedback from the mock interview) implemented it again.
🌬️ My original solution is lost in the vacuum of temporary, shared coding environments.
💅🏻 But that’s okay, this one’s better anyway.

273. Integer to English Words

The (Revised) Solution

/* convert a non-negative integer to English  
Thoughts  
1. part of this problem is naturally solved with a lookup table  
2. there's another part of the problem that feels recursive in nature  
3. we'll need to somehow peel digits or chunks of digits off of the original number for processing  
  a. we could do this by converting the integer to a string and indexing into the digits  
  b. or we could divide by, e.g. 10 and modulo to get the values  
4. we don't naturally know where we are in the number if we start at the left side (higher value digits) so it may be most natural to start at the right side (smaller value digits)  
5. teens are a special case of 2 digit numbers  
6. zero is a special case in most scenarios  
7. tens, twenties, thirties, etc are special cases  
  
Plan:  
1. repeatedly divide & modulo the number to get chunks of (up to) 3 digits to process  
2. use a lookup table for primitives & special cases  
3. keep track of how many times we've looped to insert thousands, millions, billions, etc  
  
run-time complexity: O(log(n)) (we're essentially processing digit by digit. There are log base 10 digits in a decimal integer)  
[8:47] done planning  
[30:00] done with initial implementation  
[42:00] finished 1 test case  
bug 1 found on local test (array splat on string)  
bug 2 found on local test (misspelled "Forty")  
bug 3 found on first submission (extra space after Thousand)  
bug 4 found on second submission (One Million Thousand)  
[47:58] successful submission!  
*/  
const ENGLISH = [  
  'UNUSED', // just using array as convenient number-indexed map  
  'One',  
  'Two',  
  'Three',  
  'Four',  
  'Five',  
  'Six',  
  'Seven',  
  'Eight',  
  'Nine',  
  'Ten',  
  'Eleven',  
  'Twelve',  
  'Thirteen',  
  'Fourteen',  
  'Fifteen',  
  'Sixteen',  
  'Seventeen',  
  'Eighteen',  
  'Nineteen'  
]  
  
const TENS = [  
  'UNUSED', // just using array as convenient number-indexed map  
  'UNUSED', // just using array as convenient number-indexed map  
  'Twenty',  
  'Thirty',  
  'Forty',  
  'Fifty',  
  'Sixty',  
  'Seventy',  
  'Eighty',  
  'Ninety',  
]  
  
const SUFFIX = [  
  'UNUSED', // just using array as convenient number-indexed map  
  'Thousand',  
  'Million',  
  'Billion'  
]  
  
const formatTens = (n: number): string => { // 67, 5  
  if (n < 20) {  
    return ENGLISH[n] // Five  
  } else {  
    const tens = Math.floor(n / 10) // 6  
    const ones = n % 10 // 7  
    const words = [  
      TENS[tens], // Sixty  
      ...(ones > 0 ? [ENGLISH[ones]] : []) // Seven  
    ]  
    return words.join(' ') // Sixty Seven  
  }  
}  
  
function numberToWords(num: number): string { // 1,234,567  
  if (num === 0) return 'Zero'  
  let results = []  
  for (let c = 0, n = num; n; c++) { // 2; 1; 0  
    const chunk = n % 1000 // 1; 234; 567  
    n = Math.floor(n / 1000) // 0; 1; 1,234  
    const hundreds = Math.floor(chunk / 100) // 0; 2; 5  
    const firstTwo = chunk % 100 // 1; 34; 67  
    const words = [  
      ...(hundreds ? [`${formatTens(hundreds)} Hundred`] : []), // []; Two Hundred; Five Hundred  
      ...(firstTwo ? [formatTens(firstTwo)] : []), // One; Thirty Four; Sixty Seven  
      ...((c && chunk) ? [SUFFIX[c]] : []) // Million; Thousand; []  
    ]  
    if (words.length) {  
      results.push(words.join(' ')) // [Five Hundred Sixty Seven, Two Hundred Thirty Four Thousand, One Million]  
        
    }  
  }  
  return results.reverse().join(' ') // One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven  
};  

🦶🏻 Footnote from the future: we rewrite this solution twice tomorrow.

What Happened

🏋🏻 I followed the routine I’ve been practicing on LeetCode in recent weeks. Having this go-to framework well-practiced kept the process smooth and steady.
🗣️ I narrated my thought process as I went.
🏗️ I ultimately implemented the top level function and one of two helper functions, and ran 2 manual tests on the helper function.
👉🏻 My interviewer gave feedback and tips along the way and a comprehensive review of expectations and final tips at the end.

Feedback & Takeaways

  1. Unless I find it particularly useful, I can be less verbose in my planning phase.
  2. If I have time for testing at all, a single test case chosen for good test coverage will likely suffice in an interview.
  3. To save time and space in my testing phase, I can skip variable state annotation liberally (only annotating the most valuable variables) and write the values in a comment on the same line as the variable.
  4. My performance is evaluated across 4 axes:
    1. Problem solving (i.e. my planning phase: coming up with an approach, analyzing performance, optimization)
    2. Coding (translating a plan into code, complexity, clarity)
    3. Testing
    4. Communication (ask good questions, narrate my thought process as I go)
  5. 60-70% of my evaluation is based on problem solving and coding
  6. Documentation: when stubbing helper functions, it’s useful to leave a comment describing what it should do, or maybe some input/output examples so the interviewer can easily understand what it would eventually do.
  7. If I have two problems in a single interview, the first problem is a warm up
    1. Aim to be done with it in ~15 minutes
    2. Aim for ~2 minutes of planning (ask the interviewer for help if I’m struggling to meet this bar)
    3. Aim for ~10 minutes of coding
    4. Even if I don’t have time for testing, that can be evaluated after the second problem
  8. Double the above target times for the second question in a two problem interview (i.e. 15 minutes for the first problem, 30 for the second)
  9. If a coding problem involves a lot of typing (e.g. filling out a table of English words for numbers) I can abbreviate with ellipses (e.g. ‘One’, ‘Two’, …, ‘Nineteen’)
  10. It may help to aim for a more fluid plan coming out of the planning phase to allow for pivoting or adjustment as necessary during the coding phase.
  11. If using obscure language features (e.g. + to convert a numeric string to a number), take a moment to explain this to the interviewer.

🪞 Reflections

  1. This was a very valuable exercise.
    1. I got a good sense of my current performance.
    2. I got good feedback from my interviewer (and based on my own self-observation in a realistic context) to tweak and tune my approach.
  2. I still need to figure out how to speed up my planning phase. I should spend some more time thinking about what exactly the output of my planning phase should be. For example, maybe it’s enough to mention a useful, broad approach when identified, note the runtime complexity, then move on. Some examples:
    1. Iteratively divide & modulo to get groups of digits; lookup tables for primitives & special cases - O(log(N))
    2. Breadth First Search - O(N)
    3. Two pointers / sliding window - O(N)
    4. Sorting - O(N * log(N))