Card 03/ 08

CodeDifficulty: Intermediate3 min

The One-Pass Lookup Template in Python, Java and TypeScript

One loop that looks up what the current item needs and then records the item, with the two lines that change between problems marked.

The one-pass lookup template is a single loop that works out the key the current item needs, looks that key up among the earlier items, and only then records the item. Between problems two lines change, and they are the highlighted ones: the key line, which here computes the complement target − value, and the record line, which here stores the item's index. A counting problem records a count instead, and a grouping problem computes a different key. Each listing runs on 4 1 6 3 8 with the target 9, and on three edge cases: two equal values that do form a pair, no pair at all, and an empty list.

The one-pass pair search on four inputs, in each language

pair_with_sum.pypy
def pair_with_sum(values: list[int], target: int) -> tuple[int, int] | None:
    seen: dict[int, int] = {}
    for i, value in enumerate(values):
        needed = target - value
        if needed in seen:
            return seen[needed], i
        seen[value] = i
    return None


for values, target in [([4, 1, 6, 3, 8], 9), ([5, 5], 10), ([1, 2], 10), ([], 4)]:
    pair = pair_with_sum(values, target)
    answer = "none" if pair is None else f"indices {pair[0]} {pair[1]}"
    print(f"{values} target {target}: {answer}")
OutputRan with python:3.14.8-slim-trixie (Python 3.14.8)
[4, 1, 6, 3, 8] target 9: indices 2 3
[5, 5] target 10: indices 0 1
[1, 2] target 10: none
[] target 4: none
PairWithSum.javajava
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class PairWithSum {
    static int[] pairWithSum(int[] values, int target) {
        Map<Integer, Integer> seen = new HashMap<>();
        for (int i = 0; i < values.length; i++) {
            int needed = target - values[i];
            if (seen.containsKey(needed)) {
                return new int[] {seen.get(needed), i};
            }
            seen.put(values[i], i);
        }
        return null;
    }

    public static void main(String[] args) {
        int[][] inputs = {{4, 1, 6, 3, 8}, {5, 5}, {1, 2}, {}};
        int[] targets = {9, 10, 10, 4};
        for (int k = 0; k < inputs.length; k++) {
            int[] pair = pairWithSum(inputs[k], targets[k]);
            String answer = pair == null ? "none" : "indices " + pair[0] + " " + pair[1];
            System.out.println(Arrays.toString(inputs[k]) + " target " + targets[k] + ": " + answer);
        }
    }
}
OutputRan with eclipse-temurin:25.0.4.1_1-jdk-noble (Java 25.0.4.1)
[4, 1, 6, 3, 8] target 9: indices 2 3
[5, 5] target 10: indices 0 1
[1, 2] target 10: none
[] target 4: none
pair_with_sum.tsts
function pairWithSum(values: number[], target: number): [number, number] | null {
  const seen = new Map<number, number>();
  for (let i = 0; i < values.length; i++) {
    const needed = target - values[i];
    const earlier = seen.get(needed);
    if (earlier !== undefined) {
      return [earlier, i];
    }
    seen.set(values[i], i);
  }
  return null;
}

const inputs: [number[], number][] = [
  [[4, 1, 6, 3, 8], 9],
  [[5, 5], 10],
  [[1, 2], 10],
  [[], 4],
];
for (const [values, target] of inputs) {
  const pair = pairWithSum(values, target);
  const answer = pair === null ? "none" : `indices ${pair[0]} ${pair[1]}`;
  console.log(`[${values.join(", ")}] target ${target}: ${answer}`);
}
OutputRan with node:24.22.0-trixie-slim (Node 24.22.0), TypeScript 5.9.3
[4, 1, 6, 3, 8] target 9: indices 2 3
[5, 5] target 10: indices 0 1
[1, 2] target 10: none
[] target 4: none

The three outputs match, and the first line names the pair the trace found, indices 2 and 3. The pair 5 5 shows why the lookup comes first: the second 5 finds the first, and neither finds itself. Python's dict, Java's HashMap and TypeScript's Map all give an O(1) lookup on average. The Java version reads the map twice, with containsKey and then get; the TypeScript version calls get once and tests for undefined, which is safe because every value it stores is an index. Returning null or None for no pair makes the caller decide what an absent pair means, the same decision the clarify step asks for.