Skip to content

Instantly share code, notes, and snippets.

@natecook1000
Created February 28, 2018 17:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save natecook1000/2c0ad18b7b1aa6c146468118f605a69f to your computer and use it in GitHub Desktop.
Save natecook1000/2c0ad18b7b1aa6c146468118f605a69f to your computer and use it in GitHub Desktop.
// 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