Problem overview
The method dropEnds receives an array of integers, nums, and returns a new array with every number except the first and the last. The numbers in between keep their order. The constraints give nums at least two numbers, and when it has exactly two, the answer is an empty array.
With nums = [4, 8, 15, 16, 23], the 4 and the 23 are the ends, so the answer is [8, 15, 16]. With [9, 0, 9] both 9s are ends and only the 0 is left. With [1, 2] nothing lies between the ends.
Approach
The items we want start at index 1, which skips the first number. They end at the second-to-last index, nums.count - 2. A half-open range stops just before its end, so the range 1..<nums.count - 1 covers indexes 1 up to nums.count - 2. That is exactly the middle.
For five numbers, the count is 5, the range is 1..<4, and the indexes are 1, 2 and 3.
Why not remove the two ends from a copy? That works too, but it needs two mutating calls and a var. The range does the whole job in one expression, and it never changes the input array, which is a constant inside a Swift method anyway.
Step by step
- Subscript
numswith the range1..<nums.count - 1. The result is anArraySlice, so wrap it inArray(...)to get the[Int]the method must return. Store it inmiddle. - Return
middle.
The solution in Swift
class Solution {
func dropEnds(_ nums: [Int]) -> [Int] {
let middle = Array(nums[1..<nums.count - 1])
return middle
}
}
On the three examples it prints:
[8, 15, 16]
[0]
[]
Language details
- Arithmetic binds tighter than the range operator, so
1..<nums.count - 1means1..<(nums.count - 1). - With two numbers the range is
1..<1. It is empty, which Swift allows, and the result is[]. - With fewer than two numbers the range would be reversed, and Swift stops the program. The constraints rule that out.
The reason the empty case needs no special code is that the range 1..<1 contains no indexes at all. The same line of code handles two numbers and two hundred.
Testing
Try inputs where the ends are easy to tell from the middle:
[-1, -2, -3, -4]gives[-2, -3]. Negative numbers are no different.[7, 7, 7, 7]gives[7, 7]. The values are equal, but the ends are chosen by position, not by value.[1, 2, 3]gives[2]. One number in the middle.
Edge cases and common mistakes
- Ending at
nums.count. That keeps the last number. The end of the range has to be one less. - Starting at 0. That keeps the first number.
- Using
...for the end.1...nums.count - 1includes the last index, so the last number stays. - Forgetting
Array(...). The slice is not a[Int], and a slice that starts at 1 also keeps index 1 as its first index, which surprises code that expects 0.
Another way
The library can do the same in two steps: Array(nums.dropFirst().dropLast()) gives [8, 15, 16] for the first example and [] for [1, 2]. The range version is the one this lesson practices, and it shows the index arithmetic directly.
