如何从 Swift ArraySlice 中获取第 i 个元素?

How do I fetch the i'th element from a Swift ArraySlice?

下面我试图获取 ArraySlice draggignFan 的第 i 个元素。代码构建良好(没有警告)但程序在运行时死于我尝试像普通数组一样索引切片的行:

var draggingFan : ArraySlice<Card>?
...

if let draggingFan = draggingFan {
     for i in 1 ..< draggingFan.count {
         let card = draggingFan[i] // EXECUTION ERROR HERE
         ...
     }
 }

根据 the docs 有一个 firstlast 方法(我在别处使用没有问题)。那么如何在 Swift 中索引一个 ArraySlice 呢? (注意:我故意跳过切片中的第 0 个索引——其他地方需要)。

您遇到此问题的原因是切片保留了您从中获取的序列的原始索引号。因此,元素 1 不在此切片中。

例如,考虑以下代码:

let arr = [1,2,3,4,5,6,7,8,9]
let slice = arr[2...5]

现在 slice[1] 是什么?它不是 4,即使那是切片中的第二个东西。它是 2,因为切片仍然指向原始数组。换句话说,slice[1] 超出了切片的范围!这就是您收到运行时错误的原因。

怎么办?那么,切片的实际索引是它的 indices那个 是你想要循环的。但是......你不想要切片指向的第一个元素。所以你需要推进你要迭代的范围的 startIndex 。因此:

if let draggingFan = draggingFan {
    var ixs = draggingFan.indices
    ixs.startIndex = ixs.startIndex.advancedBy(1)
    for i in ixs {
        // ... now your code will work ...
    }
}

但是,在我看来,根本没有必要为切片建立索引,您也不应该这样做。您应该循环遍历 切片本身 ,而不是遍历其索引。你有这个:

for i in 1 ..< draggingFan.count

但这很像在说

for aCard in draggingFan

...除了您想删除切片的第一个元素。那就放下吧!这么说:

for aCard in draggingFan.dropFirst()

要了解这是否有效,请在 playground 中尝试:

let arr = [1,2,3,4,5,6,7,8,9]
let slice = arr[2...5]
for anInt in slice.dropFirst() {
    print(anInt) // 4, 5, 6
}

如您所见,我们正在循环遍历所需的元素,根本没有参考索引号。

迭代切片中的元素: draggingFan?.forEach({ (元素) ... })

据我所知,获取特定元素,需要将其转换回数组,例如

let draggingFanArray = Array(draggingFan!)

这是我用来玩各种场景的游乐场代码:

进口Cocoa

var a: Array<Int>?
var b: ArraySlice<Int>?

a = [1, 2, 3, 4, 5, 6, 7]

b = a![3...5]


let count = b!.count

b!.forEach({ (element) in
    print("\(element)")

})

let c = Array(b!)
print(c[2])

编辑 ArraySlice 扩展:

extension ArraySlice {

    func elementAtIndex(index: Int)->AnyObject?{
        return Array(self)[index] as? AnyObject
    }

}

ArraySlice 的索引仍然与原始数组的索引匹配。在您的情况下,您正在访问不在您的切片中的索引 1 。如果您将索引偏移 draggingFan.startIndex 它将起作用:

if let draggingFan = draggingFan {
     for i in 1 ..< draggingFan.count {
         let card = draggingFan[draggingFan.startIndex + i]
         ...
     }
 }

或者:

if let draggingFan = draggingFan {
     for i in draggingFan.startIndex + 1 ..< draggingFan.endIndex {
         let card = draggingFan[i]
         ...
     }
 }

这将访问从切片中第二个元素到切片中最后一个元素的值:

let original = [1,2,3,4,5,6]        // Int array to demonstrate
var draggingFan : ArraySlice<Int>?
draggingFan = original[1...4]      // create the slice
if let draggingFan = draggingFan {
  // so there's no errors just slice the slice and iterate over it
  for i in draggingFan[(draggingFan.startIndex+1)..<draggingFan.endIndex] {
    print(i, terminator: ", ")
  }
}

输出:

3, 4, 5,

如果我有一个数组:

var arr = [1, 2, 3, 4, 5, 6, 7] // [1, 2, 3, 4, 5, 6, 7]

然后我取出数组的一部分:

let slice = arr[3..<arr.count] // [4, 5, 6, 7]

此切片的 startIndex 为 3,这意味着索引从 3 开始到 6 结束。

现在,如果我想要一个包含除第一个元素之外的所有元素的切片,我可以使用 dropFirst() 方法:

let sliceMinusFirst = slice.dropFirst() // [5, 6, 7]

此时,sliceMinusFirststartIndex 为 4,这意味着我的索引范围为 4 到 6。

现在,如果我想遍历这些以对项目执行某些操作,我可以执行以下操作:

for item in sliceMinusFirst {
    print(item)
}

或者,我可以用 forEach:

sliceMinusFirst.forEach { item in
    print(item)
}

通过使用这些形式的迭代,startIndex 不为零这一事实甚至都不重要,因为我不直接使用索引。没关系,在吃了一片之后,我想放下第一个项目。我能够轻松地做到这一点。我什至可以在我想进行迭代时做到这一点:

slice.dropFirst().forEach { item in
    print(item)
}

这里我从原始切片中删除了第一项,没有创建任何中间变量。

请记住,如果您需要实际使用索引,您可能做错了什么。如果您真的需要索引,请确保您了解发生了什么。

此外,如果您想在创建切片后恢复到从零开始的索引,您可以从切片创建一个数组:

let sliceArray = Array(slice) // [4, 5, 6, 7]
sliceArray.startIndex // 0