XmasRights
XmasRights

Reputation: 1509

Returning a Collection's Slice type

I'm trying to write a function that returns a Collection Slice, for the first occurrence of a given subsequence.

I've managed to write it as an Array extension, since I know that the return type would be ArraySlice<Element>, but I'm having trouble figuring out what the appropriate concrete output would be to make it a general function for Collection

extension Array where Element: Comparable {

    func firstOccuranceOf(subsequence: [Element]) -> ArraySlice<Element>? {

        guard !subsequence.isEmpty else {
            return nil
        }

        guard let startIndex = self.firstIndexOf(subsequence: subsequence) else {
            return nil
        }
        let endIndex = self.index(startIndex, offsetBy: subsequence.count - 1)
        return self[startIndex...endIndex]
    }
}

TL;DR: How do I re-write the function above as a extension Collection?

Upvotes: 1

Views: 66

Answers (2)

ielyamani
ielyamani

Reputation: 18591

First, let's fix firstOccuranceOf(subsequence:) as defined on Array:

//Only `Equatable` is needed
extension Array where Element: Equatable {
    func firstOccurence(of subsequence: [Element]) -> ArraySlice<Element>? {
        let subsequenceEndIndex = subsequence.endIndex.advanced(by: -1)
        let lastPossibleFirstIndex = endIndex.advanced(by: -subsequenceEndIndex)

        for i in indices where i < lastPossibleFirstIndex {
            let range = i...i.advanced(by: subsequenceEndIndex)
            if Array(self[range]) == subsequence {
                return self[range]
            }
        }
        return nil
    }
}

It gives:

Array(1...10).firstOccurence(of: [6, 300])  //nil
Array(1...10).firstOccurence(of: [6, 7])    //[6, 7]
Array(1...6).firstOccurence(of: [6, 7])     //nil

let array: [Int] = []
array.firstOccurence(of: [6, 7])            //nil
array.firstOccurence(of: [])                //nil

For collections in general:

extension Collection where Self: RandomAccessCollection, Element: Equatable {
    func firstOccurence(of subsequence: Self) -> Self.SubSequence? {
        let subCount = subsequence.count
        for i in indices where distance(from: i, to: endIndex) >= subCount  {
            let subseq = self.suffix(distance(from: i, to: endIndex)).prefix(subCount)
            if Array(subseq) == Array(subsequence) {
                return subseq
            }
        }
        return nil
    }
}

Upvotes: 0

David Pasztor
David Pasztor

Reputation: 54735

You just need to declare the return type as Self.SubSequence?. You'll also need to define the firstIndex(of:) method for Collections that you use in your Array extension. I've defined it just to be able to compile the code, but feel free to change the implementation if your logic was different.

extension Collection where Element: Comparable {
    func firstIndex(of subsequence: Self) -> Index? {
        guard let firstElement = subsequence.first else { return nil }
        return self.firstIndex(of: firstElement)
    }

    func firstOccurence(of subsequence: Self) -> Self.SubSequence? {
        guard !subsequence.isEmpty else { return nil }
        guard let startIndex = firstIndex(of: subsequence) else { return nil }
        let endIndex = index(startIndex, offsetBy: subsequence.count - 1)
        return self[startIndex...endIndex]
    }
}

Upvotes: 2

Related Questions