Skip to content

Instantly share code, notes, and snippets.

@SaganRitual
Created December 15, 2018 09:09
Show Gist options
  • Save SaganRitual/068175ba0e9425bea1c1f6acc73d897a to your computer and use it in GitHub Desktop.
Save SaganRitual/068175ba0e9425bea1c1f6acc73d897a to your computer and use it in GitHub Desktop.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
import Foundation
// With deepest gratitude to Stack Overflow dude:
// https://stackoverflow.com/users/97337/rob-napier
// https://stackoverflow.com/a/38413254/1610473
//
public class LoopIterator<Base: Collection>: IteratorProtocol {
let collection: Base
var index: Base.Index
private var ixOfLastReturn_: Base.Index?
fileprivate(set) var ixOfLastReturn: Base.Index {
get { guard let ix = ixOfLastReturn_ else { preconditionFailure() }; return ix }
set { ixOfLastReturn_ = newValue }
}
public init(_ collection: Base) {
self.collection = collection
self.index = collection.startIndex
}
public func next() -> Base.Iterator.Element? {
preconditionFailure("The iterator protocol requires this in the base class")
}
}
// With deepest gratitude to reddit dude
// https://www.reddit.com/user/sdker
// https://www.reddit.com/r/swift/comments/a6d1o2/trouble_with_an_iterator_over_a_collection_of/ebtwg1o
//
extension LoopIterator where Base.Element: OptionalType {
internal func compactNext() -> Base.Element {
repeat {
if let firstOptional = next() {
if let secondOptional = firstOptional {
print("Real double \(secondOptional)")
return secondOptional
}
else { print("Nil value from the input array") }
} else {
preconditionFailure("It's a looping iterator; this should never happen")
}
} while true
}
}
public class ForwardLoopIterator<Base: Collection>: LoopIterator<Base> {
public override func next() -> Base.Iterator.Element? {
guard !collection.isEmpty else { return nil }
defer {
index = collection.index(after: index)
if index == collection.endIndex { index = collection.startIndex }
}
ixOfLastReturn = index
return collection[index]
}
}
public class ReverseLoopIterator<Base: Collection>: LoopIterator<Base> {
public override func next() -> Base.Iterator.Element? {
guard !collection.isEmpty else { return nil }
let d = collection.distance(from: collection.startIndex, to: index)
let e = (d + collection.count - 1) % collection.count
if index == collection.startIndex { index = collection.endIndex }
index = collection.index(collection.startIndex, offsetBy: e)
return collection[index]
}
}
let theArray: [Double?] = [0, 1, nil, 3, 4, 5, 6, nil, 8, 9]
let iter = ForwardLoopIterator<[Double?]>(theArray)
let nextValue1 = iter.next() // This will be of type Int?.
let nextValue2 = iter.compactNext() // This will be of type Int.
print(nextValue1, nextValue2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment