Mark Bridges
Mark Bridges

Reputation: 8448

Extension on a collection type in Swift to find all the objects after an object

I'd like to write an extension on CollectionType in Swift that will find the x objects after an object in an array. Obviously it needs be protected to work even if there are no objects after the item.

In my head the signatures something like this:

func itemsAfterItem(item: T, limit: Int?) -> [T]

I can't figure out how to implement it though, could someone help?

Upvotes: 0

Views: 631

Answers (3)

luk2302
luk2302

Reputation: 57144

Just because I liked the challenge ;)

extension Array where Element : Equatable {
    func itemsAfterItem(item: Element, limit: Int? = nil) -> [Element] {
        if let from = self.indexOf(item) where from < self.count - 1 {
            if let limit = limit where from + limit < self.count {
                return Array(self[from+1...from + limit])
            }
            return Array(self[from+1...self.count-1])
        } else {
            return []
        }
    }
}

For the input

let arr = [1, 2, 4, 6, 9]

It results in

arr.itemsAfterItem(2)             // [4, 6, 9]
arr.itemsAfterItem(2, limit: 2)   // [4, 6]
arr.itemsAfterItem(2, limit: 100) // [4, 6, 9]
arr.itemsAfterItem(9, limit: 2)   // []
arr.itemsAfterItem(3, limit: 100) // []

Upvotes: 2

Martin R
Martin R

Reputation: 539915

A possible implementation for arbitrary collections of Equatable elements (explanations inline). The main challenge is to get the parameter types and constraints right.

extension CollectionType where Generator.Element: Equatable,
                         SubSequence.Generator.Element == Generator.Element {

    func itemsAfterItem(item: Generator.Element, limit: Index.Distance?) -> [Generator.Element] {
        if let idx = indexOf(item) where idx != endIndex {
            // Start after the given item:
            let from = idx.advancedBy(1)
            // Up to min(from + limit, endIndex):
            let to = limit.map { from.advancedBy($0, limit: endIndex) } ?? endIndex
            // Return slice as an array:
            return Array(self[from..<to])
        } else {
            // Item not found, or only at the last position.
            return []
        }
    }
}

Understanding the

 let to = limit.map { from.advancedBy($0, limit: endIndex) } ?? endIndex

part is left as an exercise to the reader :)

Examples:

[1, 2, 3, 4, 5, 6].itemsAfterItem(2, limit: 2)    // [3, 4]
["x", "y", "z"].itemsAfterItem("y", limit: 4)     // ["z"]
[1, 2, 3].itemsAfterItem(7, limit: 4)             // []
[1.1, 2.2, 3.3].itemsAfterItem(1.1, limit: nil)   // [2.2, 3.3]

Example for a non-array collection:

"abcdef".characters.itemsAfterItem("b", limit: 2) // ["c", "d"]

Upvotes: 3

Jimmy James
Jimmy James

Reputation: 847

I think you can try this:

func itemsAfterItem(item: T, limit: Int?) -> [T] {
    var counter: Int = 0
    var isAfter: Bool = false
    let array = [T]()
    let newLimit = limit != nil ? limit : myArray.count
    for tmpItem in myArray {
        if tmpItem == T {
            isAfter = true
        }
        if isAfter && counter < limit {
            array.append(tmpItem)
            counter += 1
        }
    }
}

This function will put your T item at the start of the array.

I've not test this function

Upvotes: 0

Related Questions