Daniel Daniel - 6 months ago 45
Swift Question

Array contains a complete subarray

In Swift how can I check whether an array contains a given subarray in its entirety? E.g., is there a

contains
function that works like this:

let mainArray = ["hello", "world", "it's", "a", "beautiful", "day"]
contains(mainArray, ["world", "it's"]) // would return true
contains(mainArray, ["world", "it"]) // would return false
contains(mainArray, ["world", "a"]) // would return false - not adjacent in mainArray

Answer

You can do it with higher-level functions, like this:

func indexOf(data:[String], _ part:[String]) -> Int? {
    // This is to prevent construction of a range from zero to negative
    if part.count > data.count {
        return nil
    }
    // The index of the match could not exceed data.count-part.count
    return (0...data.count-part.count).reduce(nil) { prev, ind in
        // If there is an earlier match "prev", return it;
        // Otherwise, construct a sub-array from current index,
        // and compare its content to what we are looking for.
        prev ?? ([String](data[ind..<ind+part.count]) == part ? ind : nil)
    }
}

This function returns the index of the first match, if any, or nil otherwise.

You can use it as follows:

let mainArray = ["hello", "world", "it's", "a", "beautiful", "day"]
if let index = indexOf(mainArray, ["world", "it's"]) {
    print("Found match at \(index)")
} else {
    print("No match")
}

Editing in as an extension to a generic array...

This can now be used for any homogeneous array of Equatable types.

extension Array where Element : Equatable {
    func indexOfContiguous(subArray:[Element]) -> Int? {
        // This is to prevent construction of a range from zero to negative
        if subArray.count > self.count {
            return nil
        }
        // The index of the match could not exceed data.count-part.count
        return (0...self.count-subArray.count).reduce(nil) { prev, ind in
            // If there is an earlier match "prev", return it;
            // Otherwise, construct a sub-array from current index,
            // and compare its content to what we are looking for.
            prev ?? ([Element](self[ind..<ind+ subArray.count]) == subArray ? ind : nil)
        }
    }
}