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

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]

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 = suffix(oneArray,2) // ["uno"]
let arr2 = suffix(twoArray,2) // ["uno", "dos"]
let arr3 = suffix(threeArray,2) // ["dos", "tres"]

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


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.


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


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