Reputation: 10432
In Swift, is there a clever way of using the higher order methods on Array to return the 5 first objects?
The obj-c way of doing it was saving an index, and for-loop through the array incrementing index until it was 5 and returning the new array. Is there a way to do this with filter
, map
or reduce
?
Upvotes: 273
Views: 177100
Reputation: 11426
Take this extension. It's fixing original horrible naming:
public extension Array {
func first(_ count: Int) -> ArraySlice<Element> {
return self.prefix(count)
}
func last(_ count: Int) -> ArraySlice<Element> {
return self.suffix(count)
}
}
usage:
someArr.first(5)
someArr.last(5)
Upvotes: 7
Reputation: 14918
By far the neatest way to get the first N elements of a Swift array is using prefix(_ maxLength: Int)
:
let array = [1, 2, 3, 4, 5, 6, 7]
let slice5 = array.prefix(5) // ArraySlice
let array5 = Array(slice5) // [1, 2, 3, 4, 5]
the one-liner is:
let first5 = Array(array.prefix(5))
This has the benefit of being bounds safe. If the count you pass to prefix
is larger than the array count then it just returns the whole array.
NOTE: as pointed out in the comments, Array.prefix
actually returns an ArraySlice
, not an Array
.
If you need to assign the result to an Array
type or pass it to a method that's expecting an Array
param, you will need to force the result into an Array
type: let first5 = Array(array.prefix(5))
Upvotes: 655
Reputation: 22343
You can do it really easy without filter
, map
, reduce
, or prefix
by just returning a range of your array via a subscript:
var wholeArray = [1, 2, 3, 4, 5, 6]
var n = 5
var firstFiveSlice = wholeArray[0..<n] // 1,2,3,4,5
let firstFiveArray = Array(firstFiveSlice)
Upvotes: 116
Reputation: 93
The Prefix function is definitely the most efficient way of solving this problem, but you can also use for-in loops like the following:
let array = [1,2,3,4,5,6,7,8,9]
let maxNum = 5
var iterationNumber = 0
var firstNumbers = [Int()]
if array.count > maxNum{
for i in array{
iterationNumber += 1
if iterationNumber <= maxNum{
firstNumbers.append(i)
}
}
firstNumbers.remove(at: 0)
print(firstNumbers)
} else {
print("There were not \(maxNum) items in the array.")
}
This solution takes up many lines of code but checks to see if there are enough items in the array to carry out the program, then continues and solves the problem.
This solution uses many basic functions including array.count
, which returns the amount of items in the array, not the position of last item in the array. It also uses array.append
, which adds things onto the end of the array. Lastly, it uses array.remove
, which removes the array's item that has a specified position.
I have tested it it and it works for at least swift 5.
Upvotes: 0
Reputation: 9848
Swift 4
To get the first N elements of a Swift array you can use prefix(_ maxLength: Int)
:
Array(largeArray.prefix(5))
Upvotes: 19
Reputation: 461
For an array of objects you can create an extension from Sequence.
extension Sequence {
func limit(_ max: Int) -> [Element] {
return self.enumerated()
.filter { $0.offset < max }
.map { $0.element }
}
}
Usage:
struct Apple {}
let apples: [Apple] = [Apple(), Apple(), Apple()]
let limitTwoApples = apples.limit(2)
// limitTwoApples: [Apple(), Apple()]
Upvotes: 8
Reputation: 92419
With Swift 5, according to your needs, you may choose one of the 6 following Playground codes in order to solve your problem.
subscript(_:)
subscriptlet array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let arraySlice = array[..<5]
//let arraySlice = array[0..<5] // also works
//let arraySlice = array[0...4] // also works
//let arraySlice = array[...4] // also works
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]
prefix(_:)
methodComplexity: O(1) if the collection conforms to RandomAccessCollection
; otherwise, O(k), where k is the number of elements to select from the beginning of the collection.
let array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let arraySlice = array.prefix(5)
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]
Apple states for prefix(_:)
:
If the maximum length exceeds the number of elements in the collection, the result contains all the elements in the collection.
prefix(upTo:)
methodComplexity: O(1)
let array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let arraySlice = array.prefix(upTo: 5)
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]
Apple states for prefix(upTo:)
:
Using the
prefix(upTo:)
method is equivalent to using a partial half-open range as the collection's subscript. The subscript notation is preferred overprefix(upTo:)
.
prefix(through:)
methodlet array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let arraySlice = array.prefix(through: 4)
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]
removeSubrange(_:)
methodComplexity: O(n), where n is the length of the collection.
var array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
array.removeSubrange(5...)
print(array) // prints: ["A", "B", "C", "D", "E"]
dropLast(_:)
methodComplexity: O(1) if the collection conforms to RandomAccessCollection
; otherwise, O(k), where k is the number of elements to drop.
let array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let distance = array.distance(from: 5, to: array.endIndex)
let arraySlice = array.dropLast(distance)
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]
Upvotes: 95
Reputation: 5451
Plain & Simple
extension Array {
func first(elementCount: Int) -> Array {
let min = Swift.min(elementCount, count)
return Array(self[0..<min])
}
}
Upvotes: 3
Reputation: 4974
Swift 4 with saving array types
extension Array {
func take(_ elementsCount: Int) -> [Element] {
let min = Swift.min(elementsCount, count)
return Array(self[0..<min])
}
}
Upvotes: 2
Reputation: 7312
SWIFT 4
A different solution:
An easy inline solution that wont crash if your array is too short
[0,1,2,3,4,5].enumerated().compactMap{ $0.offset < 3 ? $0.element : nil }
But works fine with this.
[0,1,2,3,4,5].enumerated().compactMap{ $0.offset < 1000 ? $0.element : nil }
Usually this would crash if you did this:
[0,1,2,3,4,5].prefix(upTo: 1000) // THIS CRASHES
[0,1,2,3,4,5].prefix(1000) // THIS DOESNT
Upvotes: 25
Reputation: 23976
I slightly changed Markus' answer to update it for the latest Swift version, as var
inside your method declaration is no longer supported:
extension Array {
func takeElements(elementCount: Int) -> Array {
if (elementCount > count) {
return Array(self[0..<count])
}
return Array(self[0..<elementCount])
}
}
Upvotes: 5
Reputation: 52201
let a: [Int] = [0, 0, 1, 1, 2, 2, 3, 3, 4]
let b: [Int] = Array(a.prefix(5))
// result is [0, 0, 1, 1, 2]
Upvotes: 30
Reputation: 987
For getting the first 5 elements of an array, all you need to do is slice the array in question. In Swift, you do it like this: array[0..<5]
.
To make picking the N first elements of an array a bit more functional and generalizable, you could create an extension method for doing it. For instance:
extension Array {
func takeElements(var elementCount: Int) -> Array {
if (elementCount > count) {
elementCount = count
}
return Array(self[0..<elementCount])
}
}
Upvotes: 14