Never Walk Alone
An easy Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- easy
- Seniority
- L3
- Asked in
- JPMorgan Chase
Problem
Given a text string, split on whitespace and return a list of consecutive 2-word pairs, where each pair is a list [word_i, word_{i+1}]. Return an empty list if there are fewer than 2 words.
Every step is measured against the one before it.
Example 1
Input:
text = "the quick brown fox"
Output:
[["the","quick"],["quick","brown"],["brown","fox"]]
Example 2
Input:
text = "hello world"
Output:
[["hello","world"]]
Worked solution and explanation
Strip the costume and this is a width-2 sliding window over a token stream - bigrams, the thing every NLP pipeline builds before it does anything interesting. The window part is trivial; the whole problem lives in two decisions that look cosmetic and aren't. First, how you tokenize: reach for text.split(' ') and a tab or a double space quietly injects empty-string 'words' into your output, so 'sun moon stars' comes back with ['', ''] pairs that no grader will forgive. Second, how far you loop: iterate to len(words) instead of len(words) - 1 and the last step reads words[i + 1] off the end - IndexError, on the very input the interviewer hands you to check boundaries.
How a senior reasons through it
Step 1: Tokenize with the whitespace-collapsing split
words = text.split() - no argument. I pick this deliberately over split(' ') because the inputs include tabs and double spaces, and the bare split makes runs-of-whitespace a non-issue instead of a bug I have to patch later with a filter.
Step 2: Walk adjacent index pairs, not disjoint chunks
range(len(words) - 1) gives me every starting index that has a right-hand neighbor. Each interior word appears in two pairs - as the right of one and the left of the next - because it is a SLIDING window, not two-at-a-time chunking. Getting this wrong drops half the pairs.
Step 3: Let the bound handle the short inputs
I don't write a special case for empty or single-word text. When len(words) is 0 or 1, len(words) - 1 is -1 or 0, range() is empty, and the loop never runs - so I return [] automatically. The edge case is absorbed by the loop bound instead of a guard clause I could forget.
def sequential_word_pairs(text):
words = text.split()
result = []
for i in range(len(words) - 1):
pair = [words[i], words[i + 1]]
result.append(pair)
return resultBare split() for tokenizing; range(len(words) - 1) makes the short-input case disappear.
- split(' ') - literal, fragile
- 'sun moon stars'.split(' ') -> ['sun moon', '', 'stars']. The tab stays glued inside a token and the double space yields a ''. Your pairs now contain garbage, and an empty input gives [''] (length 1, not 0).
- split() - collapsing, robust
- 'sun moon stars'.split() -> ['sun', 'moon', 'stars']. Tabs and repeated spaces collapse, edges trim, and ''.split() -> [] cleanly. Same line of code, none of the failure modes.
Common follow-up questions
- How would you generalize this to arbitrary n-grams? (Parameterize the window: loop range(len(words) - n + 1) and slice words[i:i + n]. Tests whether the off-by-one generalizes.)
- What if you needed the count of each unique bigram instead of the list? (Counter keyed by tuple(pair) - lists aren't hashable, so the tuple conversion is the tell.)
- How would you handle punctuation stuck to words, like 'fox.'? (Normalize before tokenizing - regex \w+ or str.translate - which shifts the problem from whitespace splitting to real tokenization.)