Created
February 28, 2018 17:15
-
-
Save natecook1000/2c0ad18b7b1aa6c146468118f605a69f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Speculation in regards to https://forums.swift.org/t/shorthand-for-offsetting-startindex-and-endindex/9397/133 | |
/// A pair of offsets that can be used to slice a collection. | |
/// | |
/// In order to have `RangeExpression` conformance, this would need | |
/// a phantom `Bound` type, which would unnecessarily restrict its | |
/// usage. In addition, the `RangeExpression.contains(_:)` method | |
/// couldn't be implemented without access to a collection with the | |
/// actual indices to resolve. | |
struct Offsets { | |
/// The offset of the beginning of the slice. | |
/// | |
/// When positive, the offset is from a collection's `startIndex`. | |
/// When negative, from the `endIndex`. | |
var lowerOffset: Int | |
/// The offset of the end of the slice. | |
/// | |
/// When positive, the offset is from a collection's `startIndex`. | |
/// When negative, from the `endIndex`. | |
/// When `nil`, the slice continues until the end of the collection. | |
var upperOffset: Int? | |
func relative<C>(to collection: C) -> Range<C.Index> | |
where C : Collection | |
{ | |
let start = lowerOffset < 0 | |
? collection.index(collection.endIndex, offsetBy: lowerOffset, limitedBy: collection.startIndex) ?? collection.startIndex | |
: collection.index(collection.startIndex, offsetBy: lowerOffset, limitedBy: collection.endIndex) ?? collection.endIndex | |
let end: C.Index | |
switch upperOffset { | |
case let upper?: | |
if lowerOffset.signum() == upper.signum() { | |
end = collection.index(start, offsetBy: abs(upper - lowerOffset)) | |
} else { | |
end = upper < 0 | |
? collection.index(collection.endIndex, offsetBy: upper, limitedBy: collection.startIndex) ?? collection.startIndex | |
: collection.index(collection.startIndex, offsetBy: upper, limitedBy: collection.endIndex) ?? collection.endIndex | |
} | |
case nil: | |
end = collection.endIndex | |
} | |
return start < end | |
? start..<end | |
: start..<start | |
} | |
} | |
func offsets( | |
from lower: Int = 0, to upper: Int? = nil | |
) -> Offsets { | |
return Offsets(lowerOffset: lower, upperOffset: upper) | |
} | |
extension Collection { | |
subscript(offsets: Offsets) -> SubSequence { | |
return self[offsets.relative(to: self)] | |
} | |
} | |
let numbers = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] | |
numbers[offsets(from: 2, to: 6)] // [30, 40, 50, 60] | |
numbers[offsets(from: 2, to: -2)] // [30, 40, 50, 60, 70, 80] | |
numbers[offsets(to: -2)] // [10, 20, 30, 40, 50, 60, 70, 80] | |
numbers[offsets(to: 5)] // [10, 20, 30, 40, 50] | |
numbers[offsets(from: 3)] // [40, 50, 60, 70, 80, 90, 100] | |
numbers[offsets(from: -4)] // [70, 80, 90, 100] | |
let str = "Hello, world!" | |
str[offsets(to: 3)] // "Hel" | |
str[offsets(from: 3)] // "lo, world!" | |
str[offsets(to: -5)] // "Hello, w" | |
str[offsets(from: -5)] // "orld!" | |
str[offsets(from: 3, to: -5)] // "lo, w" | |
let trim = offsets(from: 3, to: -3) | |
numbers[trim] // [40, 50, 60, 70] | |
str[trim] // "lo, wor" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment