zekel
zekel

Reputation: 9457

In Swift, what's the cleanest way to get the last two items in an Array?

Is there a cleaner way to get the last two items of an array in Swift? In general, I try to avoid this approach since it's so easy to be off-by-one with the indexes. (Using Swift 1.2 for this example.)

// Swift -- slices are kind of a hassle?
let oneArray = ["uno"]
let twoArray = ["uno", "dos"]
let threeArray = ["uno", "dos", "tres"]

func getLastTwo(array: [String]) -> [String] {
    if array.count <= 1 {
        return array
    } else {
        let slice: ArraySlice<String> = array[array.endIndex-2..<array.endIndex]
        var lastTwo: Array<String> = Array(slice)
        return lastTwo
    }
}

getLastTwo(oneArray)   // ["uno"]
getLastTwo(twoArray)   // ["uno", "dos"]
getLastTwo(threeArray) // ["dos", "tres"]

I was hoping for something closer to Python's convenience.

## Python -- very convenient slices
myList = ["uno", "dos", "tres"]
print myList[-2:] # ["dos", "tres"]

Upvotes: 61

Views: 58216

Answers (9)

matt
matt

Reputation: 535230

myList[-2:]

Yes, I have an enhancement request filed asking for negative index notation, and I suggest you file one too.

However, you shouldn't make this harder on yourself than you have to. The built-in global suffix function does exactly what you're after:

let oneArray = ["uno"]
let twoArray = ["uno", "dos"]
let threeArray = ["uno", "dos", "tres"]

let arr1 = oneArray.suffix(2) // ["uno"]
let arr2 = twoArray.suffix(2) // ["uno", "dos"]
let arr3 = threeArray.suffix(2) // ["dos", "tres"]

The result is a slice, but you can coerce it to an Array if you need to.

Upvotes: 58

mohsen
mohsen

Reputation: 5078

in swift 5 you can use suffix for get objects from the last and use prefix for get objects from the first, here is an example:

let exampleArray = ["first text", "second text", "third text"]

let arr1 = exampleArray.suffix(2) // ["second text", "third text"]
let arr2 = exampleArray.prefix(2) // ["first text", "second text"]

The result is a slice, but you can coerce it to an Array if you need to.

Upvotes: 13

Imanou Petit
Imanou Petit

Reputation: 92439

With Swift 5, according to your needs, you may choose one of the following patterns in order to get a new array from the last two elements of an array.


#1. Using Array's suffix(_:)

With Swift, objects that conform to Collection protocol have a suffix(_:) method. Array's suffix(_:) has the following declaration:

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

Returns a subsequence, up to the given maximum length, containing the final elements of the collection.

Usage:

let array = [1, 2, 3, 4]
let arraySlice = array.suffix(2)
let newArray = Array(arraySlice)
print(newArray) // prints: [3, 4]

#2. Using Array's subscript(_:)

As an alternative to suffix(_:) method, you may use Array's subscript(_:) subscript:

let array = [1, 2, 3, 4]
let range = array.index(array.endIndex, offsetBy: -2) ..< array.endIndex
//let range = array.index(array.endIndex, offsetBy: -2)... // also works
let arraySlice = array[range]
let newArray = Array(arraySlice)
print(newArray) // prints: [3, 4]

Upvotes: 105

iKK
iKK

Reputation: 7012

Swift4 solution:

let oneArray = ["uno"]
let twoArray = ["uno", "dos"]
let threeArray = ["uno", "dos", "tres"]

let arr1 = threeArray.suffix(from: threeArray.count-2) // ["dos", "tres"]

Other examples to clarify the functionality of Swift's built in function func suffix(from start: Int) -> ArraySlice<Element> are...

let arr2 = oneArray.suffix(from: 0) // ["uno"]
let arr3 = twoArray.suffix(from: 0) // ["uno", "dos"]
let arr4 = twoArray.suffix(from: 1) // ["dos"]
let arr5 = threeArray.suffix(from: 1) // ["dos", "tres"]
let arr6 = threeArray.suffix(from: 2) // ["tres"]

Upvotes: -1

Adrian Mannella
Adrian Mannella

Reputation: 99

let items = [0, 2, 5, 3, 7, 6, 9, 10]
let count = items.count
let last2 = items[count - 2 ..< count] // [9, 10]

Upvotes: 3

simons
simons

Reputation: 2400

the last two items of an array in Swift

EDIT: first checks that myArray.count >= 2

let myArray2:Array? = myArray.count >= 2 ? [myArray[myArray.count-2], myArray[myArray.count-1]] : nil

Here it is wrapped in a function which takes the array and either returns an array containing the last two or else returns nil if the passed array does not contain at least two items.

func getLastTwo(myArray:[String]) -> [String]? {
        return myArray.count >= 2 ? [myArray[myArray.count-2], myArray[myArray.count-1]] : nil
    }

Upvotes: 3

Aaron Brager
Aaron Brager

Reputation: 66242

In Swift 2, you can extend CollectionType. Here's an example (borrowing from Rob Napier's answer):

extension CollectionType {
    func last(count:Int) -> [Self.Generator.Element] {
        let selfCount = self.count as! Int
        if selfCount <= count - 1 {
            return Array(self)
        } else {
            return Array(self.reverse()[0...count - 1].reverse())
        }
    }
}

You can use it on any CollectionType. Here's Array:

let array = ["uno", "dos", "tres"]
print(array.last(2)) // [dos, tres]

Here's CharacterView:

let string = "looking"
print(string.characters.last(4)) // [k, i, n, g]

(Note that my example returns an Array in all cases, not the original collection type.)

Upvotes: 7

zrzka
zrzka

Reputation: 21239

More generic answer ...

let a1 = [1,2,3,4,5]
let a2 = ["1","2","3","4","5"]

func getLast<T>(array: [T], count: Int) -> [T] {
  if count >= array.count {
    return array
  }
  let first = array.count - count
  return Array(array[first..<first+count])
}

getLast(a1, count: 2) // [4, 5]
getLast(a2, count: 3) // ["3", "4", "5"]

Upvotes: 3

Rob Napier
Rob Napier

Reputation: 299345

I doubt it's going to make you that much happier, but the math is certainly simpler:

func getLastTwo(array: [String]) -> [String] {
    if array.count <= 1 {
        return array
    } else {
        return array.reverse()[0...1].reverse()
    }
}

Note that reverse() is lazy, so this isn't particularly expensive.

Upvotes: 3

Related Questions