Problem overview
The method moveToFront gets a to-do list items (a list of strings) and a number index, and returns the list with the item at that index moved to the front. The item is taken out of its place and put at index 0, and the other items keep their order. The index is always valid, and the same word may appear more than once.
Examples:
["wash", "shop", "cook", "read"]withindex = 2gives["cook", "wash", "shop", "read"].["tea", "coffee"]withindex = 0gives["tea", "coffee"], because the item is already first.["call", "pay", "call", "walk"]withindex = 2gives["call", "call", "pay", "walk"]. The "call" at index 2 moves, not the one at index 0.
Approach: take it out, then put it in front
Two list methods do the whole job. items.pop(index) removes the item at that position and returns it, and the items behind it close the gap. items.insert(0, item) then puts it at index 0, and everything else moves one place to the right. The items that were before the moved one are pushed right by the insert, and the ones after it were never disturbed, so their relative order holds.
The order of the two calls matters. The index refers to the original list, so the item must be read and removed first. If you inserted first, the list would be one longer and every position before the old one would shift.
Step by step
- Remove the item at
indexwithpopand keep what it returns initem. - Insert
itemat index 0 withinsert(0, item). - Return
items, which has been changed in place.
The solution in Python
class Solution:
def moveToFront(self, items: List[str], index: int) -> List[str]:
item = items.pop(index)
items.insert(0, item)
return items
On the three examples it prints:
['cook', 'wash', 'shop', 'read']
['tea', 'coffee']
['call', 'call', 'pay', 'walk']
The method changes the list it receives and returns that same list. Anyone holding the original variable sees the move too. If you need the old order kept, work on a copy first, with items = items[:] or list(items). The lesson's tests only look at what the method returns.
Another way: build a new list
You can also leave the input alone and build the answer from three pieces: the chosen item, everything before it, and everything after it.
def move_to_front(items, index):
return [items[index]] + items[:index] + items[index + 1:]
For the third example this gives ['call', 'call', 'pay', 'walk'], and the list passed in is still ['call', 'pay', 'call', 'walk'] afterwards. The slice items[:index] holds the items before the chosen one, and items[index + 1:] holds the ones after it. Both versions return the same list for the same input; they differ in whether the argument is changed.
Edge cases and common mistakes
- Removing by value.
items.remove(items[index])deletes the first equal item, not the one atindex. For the third example it removes the "call" at index 0, so the list comes out unchanged:['call', 'pay', 'call', 'walk']. - Inserting first and then popping the same index. After
items.insert(0, items[2])the list is longer, anditems.pop(2)removes the wrong item. For the first example it leaves['cook', 'wash', 'cook', 'read']. - Index 0. It needs no special case: the item is popped and inserted at the same place.
- A one-item list. The pop leaves an empty list and the insert puts the item back, so it works as well.
- Using
appendby habit.appendadds at the end. To put an item at the front you needinsert(0, item).
Testing
Try moving the last item (index = len(items) - 1), which should come out first with the rest shifted right, and a one-item list, which should come back unchanged.
