276

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?

1
  • See my answer for Swift 4 that shows up to 6 different ways to return the first n elements of an Array. Commented Jul 7, 2017 at 10:19

13 Answers 13

658

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))

Sign up to request clarification or add additional context in comments.

8 Comments

+1 such a bad name choice, IMHO. Array has dropFirst and dropLast, so might as well have takeFirst and takeLast.
Also if you need first5 to be an array, just write let first5 = Array(someArray.prefix(5))
@mluisbrown Sorry, can't agree with you :) In my project video = video.prefix(5) in Xcode 7.2 results in compile error Cannot assign value of type 'ArraySlice<Video>' to type '[Video]'
video = Array(video.prefix(5))
@onmyway133 prefix is possibly Swift 2.x only (I don't remember if it was in 1.x), but it certainly exists in any OS that supports Swift 2.x, which is iOS 7 and above. Swift feature availability is determined by Swift releases, not iOS versions.
|
117

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)

5 Comments

If you want just the first n items from a Swift array you can do wholeArray.prefix(n) which has the added benefit of being bounds safe. If n is larger than the array size prefix returns the whole array.
It will crash if the wholeArray doesn't have more elements than n
@Crashalot I'm not entirely sure, but I think at the time, where I've written my answer, there was no such thing as prefix.
Use this code to get first 5 objects... This works perfectly [0,1,2,3,4,5].enumerated().flatMap{ $0 < 5 ? $1 : nil }
WARNING: with this code you can get "Fatal error: Array index is out of range" in lot of cases. In my answer you can get more safe code.
95

With Swift 5, according to your needs, you may choose one of the 6 following Playground codes in order to solve your problem.


#1. Using subscript(_:) subscript

let 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"]

#2. Using prefix(_:) method

Complexity: 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.


#3. Using prefix(upTo:) method

Complexity: 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 over prefix(upTo:).


#4. Using prefix(through:) method

let 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"]

#5. Using removeSubrange(_:) method

Complexity: 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"]

#6. Using dropLast(_:) method

Complexity: 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"]

1 Comment

WARNING: with subscript code you can get "Fatal error: Array index is out of range" in lot of cases. In my answer you can get more safe code.
30
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]

Comments

25

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

2 Comments

for swift3 [0,1,2,3,4,5].enumerated().flatMap{ $0.offset < 1000 ? $0.element : nil }
Thanks! In case you want to update, flatMap is now called compactMap in Swift 4 for array compaction.
19

Swift 4

To get the first N elements of a Swift array you can use prefix(_ maxLength: Int):

Array(largeArray.prefix(5))

Comments

14

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])
    }
}

1 Comment

1) there are exist stock function ".prefix()" ; 2)will crash in case of 0 elements; 3) you create new array instead of ArraySlice, so it take more memory than it must to :)
8

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()]

1 Comment

1) there is exist stock func for arrays called ".prefix()" 2) using of your func will be the reason of extra memory usage
8

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)
    }

    func take(_ range: ClosedRange<Int>) -> ArraySlice<Element> {
        guard self.count > 0 else { return [] }
        
        let lower = Swift.max(0, range.lowerBound)
        let upper = Swift.min(self.count - 1, range.upperBound)
        
        guard lower <= upper else { return [] }
        
        return self[lower...upper]
    }
}

usage:

someArr.first(5)
someArr.last(5)

someArr.take(0...5)

Why "take()" function needed?

Because of you will get Fatal error: Array index is out of range in lot of use-cases. But with "take()" you will not.

Comments

5

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])
    }
}

1 Comment

1)will crash in case 0 elements; 2) there are exist stock function ".prefix()" ; 3) you create new array instead of ArraySlice, so it take more memory than it must to :)
3

Plain & Simple

extension Array {
    func first(elementCount: Int) -> Array {
          let min = Swift.min(elementCount, count)
          return Array(self[0..<min])
    }
}

1 Comment

1)will crash in case 0 elements; 2) there are exist stock function ".prefix()" ; 3) you create new array instead of ArraySlice, so it take more memory than it must to :)
2

Swift 4 with saving array types

extension Array {
    func take(_ elementsCount: Int) -> [Element] {
        let min = Swift.min(elementsCount, count)
        return Array(self[0..<min])
    }
}

1 Comment

1)will crash in case 0 elements; 2) there are exist stock function ".prefix()" ; 3) you create new array instead of ArraySlice, so it take more memory than it must to :)
0

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.

1 Comment

there is exist stock func for arrays called ".prefix()" :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.