Search code examples
arraysswiftswift2swift-extensions

Convert Array extension to SequenceType?


I'm putting together some common functions I use in my app and came up with the below extensions:

public extension CollectionType {
    public func toArray() -> [Self.Generator.Element] {
        return self.map { $0 }
    }
}

public extension Array {

    public func any(fn: (Element) -> Bool) -> Bool {
        return self.filter(fn).count > 0
    }

    public func all(fn: (Element) -> Bool) -> Bool {
        return self.filter(fn).count == self.count
    }

    public func take(count:Int) -> [Element] {
        var to = [Element]()
        var i = 0
        while i < self.count && i < count {
            to.append(self[i++])
        }
        return to
    }

    public func skip(count:Int) -> [Element] {
        var to = [Element]()
        var i = count
        while i < self.count {
            to.append(self[i++])
        }
        return to
    }
}

Can they be applied to a lower level type like SequenceType? Also, do I have to put @noescape anywhere in these functions?


Solution

  • Generally speaking, @noescape should be used anytime the closure isn't saved for later usage, it seems appropriate for both of the closures you have here.

    It's not really clear what you mean by "can they be applied to a lower level type" More or less obviously, you could create versions of these extensions for some of the other protocols, but your existing skip function can only be applied to Array (btw, there's an existing function dropFirst on SequenceType that does exactly the same thing)

    For both take and skip you might want to actually consider returning an ArraySlice as this avoids copying the original array:

    extension Array {
        func take(count:Int) -> ArraySlice<Element> {
            return self[0..<count]
        }
    
        func drop(count:Int) -> ArraySlice<Element> {
            return self[count..<self.count]
        }
    }
    

    Note that for both of these, you probably (may) want to add some error detection/handling as they'll blow up if count > self.count.

    Likewise, using contains to implement any and all is probably more efficient since it doesn't result in building a new array just for the count:

    extension Array {
        func any(@noescape predicate:(Element)->Bool) -> Bool {
            return contains(predicate)
        }
        func all(@noescape predicate:(Element)->Bool) -> Bool {
            return !contains { !predicate($0) }
        }
    }
    

    As an example of defining some of these as extensions to SequenceType:

    extension SequenceType {
        func any(@noescape predicate:(Generator.Element)->Bool) -> Bool {
            return contains(predicate)
        }
        func all(@noescape predicate:(Generator.Element)->Bool) -> Bool {
            return !contains { !predicate($0) }
        }
        func drop(count:Int) -> Self.SubSequence {
            return self.dropFirst(count)
        }
    }
    

    And, as an example of implementing take as a Sequence extension:

    struct TakeFromSequenceSequence<S:SequenceType> : SequenceType {
        var limit : Int
        var sequence : S
    
        func generate() -> AnyGenerator<S.Generator.Element> {
            var generator = sequence.generate()
            var limit = self.limit
    
            return anyGenerator {
                guard limit > 0 else {
                    return nil
                }
    
                limit = limit - 1
    
                return generator.next()
            }
        }
    }
    
    
    extension SequenceType {
        func take(count:Int) -> TakeFromSequenceSequence<Self> {
            return TakeFromSequenceSequence(limit: count, sequence: self)
        }
    }