161

How can I do something like this? Take the first n elements from an array:

newNumbers = numbers[0..n]

Currently getting the following error:

error: could not find an overload for 'subscript' that accepts the supplied arguments

EDIT:

Here is the function that I'm working in.

func aFunction(numbers: Array<Int>, position: Int) -> Array<Int> {
    var newNumbers = numbers[0...position]
    return newNumbers
}
Charlie Egan
  • 4,878
  • 6
  • 33
  • 48

5 Answers5

223

This works for me:

var test = [1, 2, 3]
var n = 2
var test2 = test[0..<n]

Your issue could be with how you're declaring your array to begin with.

EDIT:

To fix your function, you have to cast your Slice to an array:

func aFunction(numbers: Array<Int>, position: Int) -> Array<Int> {
    var newNumbers = Array(numbers[0..<position])
    return newNumbers
}

// test
aFunction([1, 2, 3], 2) // returns [1, 2]
Cezary Wojcik
  • 21,745
  • 6
  • 36
  • 36
  • I've add a little more context to my question, I'm working inside a function. This works independently, but not inside the function. – Charlie Egan Jun 04 '14 at 10:06
  • 1
    Lovely! Cheers - that works. I'll accept the answer when it lets me. – Charlie Egan Jun 04 '14 at 10:11
  • 30
    Pedantic point, but this isn't really *casting* the `Slice` to an `Array`, but rather creating a new array from a slice. A cast would use the `as` operator: `numbers as Array` which would result in an error. – j b Jun 10 '14 at 09:04
  • The language has changed, it uses three ellipsis and not two https://developer.apple.com/library/ios/documentation/General/Reference/SwiftStandardLibraryReference/Array.html – Daniel Galasko Jan 28 '15 at 15:53
  • 4
    The two dots`..` changed to `..<`; the three dots (an ellipsis) have stayed the same. Thanks for the reminder, though; I updated the answer. – Cezary Wojcik Jan 28 '15 at 17:28
  • `...` is to including end index, `..<` to not including end index – Rich Fox Nov 23 '15 at 18:46
145

#1. Using Array subscript with range

With Swift 5, when you write…

let newNumbers = numbers[0...position]

newNumbers is not of type Array<Int> but is of type ArraySlice<Int>. That's because Array's subscript(_:​) returns an ArraySlice<Element> that, according to Apple, presents a view onto the storage of some larger array.

Besides, Swift also provides Array an initializer called init(_:​) that allows us to create a new array from a sequence (including ArraySlice).

Therefore, you can use subscript(_:​) with init(_:​) in order to get a new array from the first n elements of an array:

let array = Array(10...14) // [10, 11, 12, 13, 14]
let arraySlice = array[0..<3] // using Range
//let arraySlice = array[0...2] // using ClosedRange also works
//let arraySlice = array[..<3] // using PartialRangeUpTo also works
//let arraySlice = array[...2] // using PartialRangeThrough also works
let newArray = Array(arraySlice)
print(newArray) // prints [10, 11, 12]

#2. Using Array's prefix(_:) method

Swift provides a prefix(_:) method for types that conform to Collection protocol (including Array). prefix(_:) has the following declaration:

func prefix(_ maxLength: Int) -> ArraySlice<Element>

Returns a subsequence, up to maxLength in length, containing the initial elements.

Apple also states:

If the maximum length exceeds the number of elements in the collection, the result contains all the elements in the collection.

Therefore, as an alternative to the previous example, you can use the following code in order to create a new array from the first elements of another array:

let array = Array(10...14) // [10, 11, 12, 13, 14]
let arraySlice = array.prefix(3)
let newArray = Array(arraySlice)
print(newArray) // prints [10, 11, 12]
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
  • 4
    For the sake of completeness, `Array` also has a `suffix` method that works like the `prefix` method shown here. – arlomedia Dec 17 '20 at 00:36
13

subscript

extension Array where Element : Equatable {
  public subscript(safe bounds: Range<Int>) -> ArraySlice<Element> {
    if bounds.lowerBound > count { return [] }
    let lower = Swift.max(0, bounds.lowerBound)
    let upper = Swift.max(0, Swift.min(count, bounds.upperBound))
    return self[lower..<upper]
  }
  
  public subscript(safe lower: Int?, _ upper: Int?) -> ArraySlice<Element> {
    let lower = lower ?? 0
    let upper = upper ?? count
    if lower > upper { return [] }
    return self[safe: lower..<upper]
  }
}

returns a copy of this range clamped to the given limiting range.

var arr = [1, 2, 3]
    
arr[safe: 0..<1]    // returns [1]  assert(arr[safe: 0..<1] == [1])
arr[safe: 2..<100]  // returns [3]  assert(arr[safe: 2..<100] == [3])
arr[safe: -100..<0] // returns []   assert(arr[safe: -100..<0] == [])

arr[safe: 0, 1]     // returns [1]  assert(arr[safe: 0, 1] == [1])
arr[safe: 2, 100]   // returns [3]  assert(arr[safe: 2, 100] == [3])
arr[safe: -100, 0]  // returns []   assert(arr[safe: -100, 0] == [])
Phil
  • 325
  • 3
  • 7
3

Array functional way:

   array.enumerated().filter { $0.offset < limit }.map { $0.element }

ranged:

 array.enumerated().filter { $0.offset >= minLimit && $0.offset < maxLimit }.map { $0.element }

The advantage of this method is such implementation is safe.

Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
0

One more variant using extension and argument name range

This extension uses Range and ClosedRange

extension Array {

    subscript (range r: Range<Int>) -> Array {
        return Array(self[r])
    }


    subscript (range r: ClosedRange<Int>) -> Array {
        return Array(self[r])
    }
}

Tests:

func testArraySubscriptRange() {
    //given
    let arr = ["1", "2", "3"]

    //when
    let result = arr[range: 1..<arr.count] as Array

    //then
    XCTAssertEqual(["2", "3"], result)
}

func testArraySubscriptClosedRange() {
    //given
    let arr = ["1", "2", "3"]

    //when
    let result = arr[range: 1...arr.count - 1] as Array

    //then
    XCTAssertEqual(["2", "3"], result)
}
yoAlex5
  • 29,217
  • 8
  • 193
  • 205