我正在尝试分割数组的安全方法。
我知道你可以把一个数组的子集做成这样的事情:
let arr = [1,2,3,4,5]
print(arr[0..<3])
它会打印[1,2,3]
如果您在同一arr
print(arr[3..<9])
程序将崩溃
我想制作一个不会崩溃的数组扩展,尽可能多地生成元素,以便打印
[4,5]
subscript(safe range: Range) -> Element? {
}
答案 0 :(得分:5)
您可以执行以下操作:
extension Array {
subscript(safe range: Range<Index>) -> ArraySlice<Element>? {
if range.endIndex > endIndex {
if range.startIndex >= endIndex {return nil}
else {return self[range.startIndex..<endIndex]}
}
else {
return self[range]
}
}
}
let a = [1,2,3]
a[safe: 1...3] // [2,3]
编辑:鉴于起始索引可能不是数组的开头的注释,我进行了修改,以便返回的切片始终从startIndex开始,即使endIndex超出了数组的边界(除非开始索引在数组的endIndex之后,在这种情况下返回nil)。
答案 1 :(得分:3)
您可以使用
extension RandomAccessIndexType {
@warn_unused_result
public func advancedBy(n: Self.Distance, limit: Self) -> Self
}
将给定范围限制为给定数组的有效范围的方法:
extension Array {
public subscript (safe subRange: Range<Int>) -> ArraySlice<Element> {
let from = startIndex.advancedBy(subRange.startIndex, limit: endIndex)
let to = startIndex.advancedBy(subRange.endIndex, limit: endIndex)
return self[from ..< to]
}
}
let arr = [1,2,3,4,5]
print(arr[safe: 3..<10]) // [4, 5]
print(arr[safe: 9..<10]) // []
Swift 3更新:索引集合发生了很大变化。
现在,您可以使用中定义的index(...)
方法
BidirectionalIndexable
协议:
extension Array {
public subscript (safe subRange: Range<Int>) -> ArraySlice<Element> {
let from = index(startIndex, offsetBy: subRange.lowerBound, limitedBy: endIndex) ?? endIndex
let to = index(startIndex, offsetBy: subRange.upperBound, limitedBy: endIndex) ?? endIndex
return self[from ..< to]
}
}
答案 2 :(得分:3)
编辑:更新为更直接的版本。
刚为练习做好准备。与其他用于清晰度的safe
命名相同;请注意,它不会返回nil,而是返回一个用于越界索引的空数组,这可以避免在许多情况下对使用代码进行空检查。
extension Array {
subscript(safe range: Range<Index>) -> ArraySlice<Element> {
return self[min(range.startIndex, self.endIndex)..<min(range.endIndex, self.endIndex)]
}
}
let a = [1,2,3]
a[safe: 1..<17] // [2,3]
a[safe: 4..<17] // []
a[safe: 1..<2] // [2]
...或替代 - 更直接 - 版本;
答案 3 :(得分:0)
来自Array
&#39; startIndex
的文档:
因此可以假设始终为零,这是非空时第一个元素的索引。
startIndex
是第一个元素的索引(如果有的话)。
extension Array {
subscript(safe range: Range<Index>) -> [Element]? {
guard range.startIndex >= self.startIndex else { return nil }
guard range.endIndex <= self.endIndex else { return nil }
return Array(self[range])
}
}
[1, 2, 3, 4, 5][safe: 1..<4] // [2, 3, 4]
[1, 2, 3, 4, 5][safe: 1...7] // nil
答案 4 :(得分:0)
我们可以如下概括马丁对CollectionType
s的精细解决方案。此外,我认为我对take(_: Range<Index>)
这样的签名更为舒服,因为take(_: Int)
传统上同样宽容超出范围语义(与集合&#39; subscript
方法相反):
public extension CollectionType where Index.Distance : protocol<IntegerLiteralConvertible, Comparable> {
public func take(range: Range<Index>) -> SubSequence {
let dFrom = startIndex.distanceTo(range.startIndex)
let dTo = startIndex.distanceTo(range.endIndex)
let from = dFrom <= 0 ? startIndex : startIndex.advancedBy(dFrom, limit: endIndex)
let to = dTo <= 0 ? startIndex : startIndex.advancedBy(dTo, limit: endIndex)
return self[from..<to]
}
}
public extension CollectionType {
public var array: [Generator.Element] { return Array(self) }
}
let collection = 1...5
collection.take(9...10).array // []
collection.take(1...3).array // [2, 3, 4]
collection.take((-3)...10).array // [1, 2, 3, 4, 5]
collection.take((-5)...(-2)).array // []
仅需要约束Index.Distance : protocol<IntegerLiteralConvertible, Comparable>
才能优雅地处理小于startIndex
的索引范围......
请注意,将此应用于String.characters
需要:
let s = "random string"
s.characters.take(s.startIndex.advancedBy(3)...s.startIndex.advancedBy(5)).array // ["d", "o", "m"]
不太好。但是,除了上面更通用的代码之外,我们还可以提供take(_: Range<Int>)
替代方案:
public extension CollectionType where Index.Distance == Int {
public func take(range: Range<Int>) -> SubSequence {
let from = range.startIndex <= 0 ? startIndex : startIndex.advancedBy(range.startIndex, limit: endIndex)
let to = range.endIndex <= 0 ? startIndex : startIndex.advancedBy(range.endIndex, limit: endIndex)
return self[from..<to]
}
}
s.characters.take(3...5).array // ["d", "o", "m"]
答案 5 :(得分:0)
与可选集合相比,您应该更喜欢空集合。快速4效果很好。
extension Array {
/*
Safe array access via range, accounting for array bounds
examples:
[1, 2, 3][0...6] -> []
[1, 2, 3][0...1] -> [1, 2]
*/
subscript(safe range: Range<Index>) -> [Element] {
guard
range.startIndex >= self.startIndex,
range.endIndex <= self.endIndex
else {
return []
}
return Array(self[range])
}
}