bogen
bogen

Reputation: 10432

How to return first 5 objects of Array in Swift?

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

Answers (13)

Andrew_STOP_RU_WAR_IN_UA
Andrew_STOP_RU_WAR_IN_UA

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

mluisbrown
mluisbrown

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

Christian
Christian

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

NDCoder
NDCoder

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

quemeful
quemeful

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

Leo Valentim
Leo Valentim

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

Imanou Petit
Imanou Petit

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.


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

Upvotes: 95

Abhishek Bedi
Abhishek Bedi

Reputation: 5451

Plain & Simple

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

Upvotes: 3

Pavel Shorokhov
Pavel Shorokhov

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

David Rees
David Rees

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

Antoine
Antoine

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

neoneye
neoneye

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

Markus
Markus

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

Related Questions