S2Q1 · Remove K Consecutive Duplicates¶
⚡ Quick Reference
Function: remove_k_consecutive_duplicates(nums: list, k: int) -> list
Core idea: group consecutive identical elements using itertools.groupby, keep only groups with fewer than k elements.
from itertools import groupby
def remove_k_consecutive_duplicates(nums: list, k: int) -> list:
result = []
for val, group in groupby(nums):
items = list(group)
if len(items) < k:
result.extend(items)
return result
Key rules:
- Groups of < k consecutive identical elements are kept
- Groups of >= k consecutive identical elements are removed entirely
- Non-consecutive duplicates are treated as separate groups
Problem Statement¶
Problem
Write a function remove_k_consecutive_duplicates(nums, k) that removes all elements belonging to a consecutive run of length >= k. Elements in shorter runs are kept.
Example:
nums=[1, 1, 1, 2, 2, 3, 3, 3, 3], k=3
[2, 2]
Understanding the problem¶
Group consecutive identical elements:
[1, 1, 1, 2, 2, 3, 3, 3, 3]
↑──────↑ ↑───↑ ↑────────↑
run=3 run=2 run=4
k=3: keep runs with length < 3
run of 1 (len=3): 3 >= 3 → REMOVE
run of 2 (len=2): 2 < 3 → KEEP → [2, 2]
run of 3 (len=4): 4 >= 3 → REMOVE
Result: [2, 2] ✓
itertools.groupby groups consecutive identical elements
groupby(nums) yields (value, group_iterator) pairs for each consecutive run. list(group) collects the run into a list whose length can be checked against k.
Tracing the example¶
nums = [1, 1, 1, 2, 2, 3, 3, 3, 3], k = 3
val |
items |
len(items) |
< k? |
Added to result |
|---|---|---|---|---|
| 1 | [1,1,1] |
3 | ❌ | - |
| 2 | [2,2] |
2 | ✅ | [2, 2] |
| 3 | [3,3,3,3] |
4 | ❌ | - |
Result: [2, 2] ✓
More examples to build intuition¶
remove_k_consecutive_duplicates([1, 1, 2, 2, 2, 3], 3)
# groups: [1,1](2), [2,2,2](3), [3](1)
# keep: len<3 → [1,1] and [3]
# → [1, 1, 3]
remove_k_consecutive_duplicates([5, 5, 5, 5], 2)
# one group: [5,5,5,5](4), 4 >= 2 → remove all
# → []
remove_k_consecutive_duplicates([1, 2, 1, 2], 2)
# groups: [1](1), [2](1), [1](1), [2](1) - all length 1
# 1 < 2 → all kept
# → [1, 2, 1, 2]
The last example shows that non-consecutive duplicates are treated as separate groups.
Solution approaches¶
def remove_k_consecutive_duplicates(nums: list, k: int) -> list:
if not nums:
return []
result = []
count = 1
for i in range(1, len(nums)):
if nums[i] == nums[i - 1]:
count += 1
else:
if count < k:
result.extend([nums[i - 1]] * count)
count = 1
# Handle the last group
if count < k:
result.extend([nums[-1]] * count)
return result
Count each run manually. When a value changes (or we reach the end), decide whether to keep the run.
from itertools import groupby
def remove_k_consecutive_duplicates(nums: list, k: int) -> list:
return [
val
for val, group in groupby(nums)
for val in list(group)
if len(list(group)) < k # ⚠️ groupby iterator is consumed - use manual approach
]
groupby iterator can only be consumed once
The pattern above has a bug - list(group) consumes the iterator the first time, making len(list(group)) return 0 on the second call. Always convert to a list first and reuse it.
Correct version:
from itertools import groupby
def remove_k_consecutive_duplicates(nums: list, k: int) -> list:
groups = [(val, list(g)) for val, g in groupby(nums)]
kept = filter(lambda vg: len(vg[1]) < k, groups)
return [v for _, items in kept for v in items]
filter(lambda vg: len(vg[1]) < k, groups) keeps only groups shorter than k. The final comprehension flattens them back into a list.
Key takeaways¶
itertools.groupby for consecutive runs
groupby(nums) splits a list into consecutive runs of identical values. It only groups consecutive elements - [1,2,1] gives three groups, not two.
Convert groupby iterator to list immediately
The group iterator from groupby can only be consumed once. Always do items = list(group) and reuse items - calling list(group) twice returns an empty list the second time.
extend() to add all items of a run
result.extend(items) adds all elements of the kept run to the result. Using append(items) would add the list as a single element - use extend to flatten.