Problem overview
The method firstN receives an array of integers, nums, and a number n. It returns a new array holding the first n numbers, in the same order. When n is 0 the result is an empty array, and when n equals the length of nums every number is kept.
With nums = [3, 8, 1, 9, 4] and n = 3, the answer is [3, 8, 1]: the 9 and the 4 are left out. The constraints promise that n is between 0 and the length of nums, so you never have to handle a number that is too big.
Approach
The first n items sit at indexes 0 up to n - 1. The half-open range 0..<n stops just before n, so it covers exactly those n indexes. Putting that range in the array's subscript picks the items. No loop is needed.
Step by step
- Take the items of
numsin the range0..<n. The result of that subscript is anArraySlice, a view of part ofnums, not an array. The method must return[Int], so wrap the slice inArray(...), which copies the items into a new array. Store it inresult. - Return
result.
The solution in Swift
class Solution {
func firstN(_ nums: [Int], _ n: Int) -> [Int] {
let result = Array(nums[0..<n])
return result
}
}
Running it on the three examples:
[3, 8, 1]
[]
[2, 4]
Those lines come from nums = [3, 8, 1, 9, 4] with n = 3, nums = [5, 6, 7] with n = 0, and nums = [2, 4] with n = 2.
Language details
- When
nis 0,0..<0is an empty range. Slicing with it is allowed and gives an empty slice, soresultis[]. - When
nequalsnums.count, the range ends at the count, one past the last index. That is how a range reaches the end of an array. - The input array is not changed.
resultis a separate copy.
Testing
A few more inputs are worth trying, because they sit at the edges:
nums = [-5, 0, 7],n = 1gives[-5]. Negative numbers are copied like any other.nums = [10],n = 1gives[10]: a single item, all kept.nums = [10],n = 0gives[]: a single item, none kept.
Slices matter here for one more reason. A slice keeps the indexes of the array it came from, so a slice that starts at 0, like this one, happens to look like an array, while a slice that starts later does not. Wrapping in Array(...) removes that surprise for every case.
Edge cases and common mistakes
- Forgetting
Array(...). The method promises[Int], and anArraySlice<Int>is a different type. The compiler stops withcannot convert return expression of type 'ArraySlice<Int>' to return type '[Int]'. - Using
0...n. The closed range includes indexn, which givesn + 1items, and it crashes whennis the full length. Use..<. - Looking for a method that returns an optional. Nothing is optional here. The subscript gives items directly.
Cost
The work is one copy of at most n numbers, and the constraints keep the array to ten items. Nothing here depends on cleverness, only on picking the right range.
Another way
The library has prefix(_:), which also returns a slice. Array(nums.prefix(n)) gives the same result for these inputs, and it doesn't crash when n is larger than the array. The range version is the one this lesson practices.
