什么是等同于NSMutableArray
的{{1}}的Swift数组?逐个删除每个索引不起作用,因为删除一个索引后剩余的索引将会移位。什么是实现此功能的有效方法?
答案 0 :(得分:16)
我喜欢纯粹的Swift解决方案,即不使用NSIndexSet:
extension Array {
mutating func removeAtIndexes (ixs:[Int]) -> () {
for i in ixs.sorted(>) {
self.removeAtIndex(i)
}
}
}
编辑在Swift 4中将是:
extension Array {
mutating func remove (at ixs:[Int]) -> () {
for i in ixs.sorted(by: >) {
self.remove(at:i)
}
}
}
但是在我写完这个答案的几年后,WWDC 2018 Embracing Algorithms视频指出了这个缺陷:它是O(n 2 ),因为remove(at:)
本身必须遍历数组。
根据该视频,Swift 4.2 removeAll(where:)
是有效的,因为它使用半稳定分区。所以我们可以这样写:
extension Array {
mutating func remove(at set:IndexSet) {
var arr = Swift.Array(self.enumerated())
arr.removeAll{set.contains($0.offset)}
self = arr.map{$0.element}
}
}
我的tests表明,尽管重复contains
,但速度提高了100倍。但是,@ vadian的方法比 快10倍,因为他通过巧妙地走在索引集中同时展开contains
(使用半稳定)分区)。
答案 1 :(得分:12)
根据WWDC 2018 Session 223 – Embracing Algorithms,一个有效的解决方案是半稳定分区算法:
extension Array {
mutating func remove(at indexes : IndexSet) {
guard var i = indexes.first, i < count else { return }
var j = index(after: i)
var k = indexes.integerGreaterThan(i) ?? endIndex
while j != endIndex {
if k != j { swapAt(i, j); formIndex(after: &i) }
else { k = indexes.integerGreaterThan(k) ?? endIndex }
formIndex(after: &j)
}
removeSubrange(i...)
}
}
仅通过交换元素将所有不在索引集中的元素移动到数组的末尾。 半稳定表示算法保留左侧分区的顺序,但不关心右侧的顺序,因为无论如何都会删除元素。在循环之后,变量i
包含要删除的项的第一个索引。批量删除操作很便宜,因为不会移动/重建索引。
例如,如果你有一个数组
[0, 1, 2, 3, 4, 5, 6, 7]
并且您要删除索引2
和4
处的元素,算法会在while
循环中执行以下步骤(索引j
的初始值为第一个要删除的索引后的索引):
3
:转换索引2
和3
→[0, 1, 3, 2, 4, 5, 6, 7]
4
:无变化5
:转换索引3
和5
→[0, 1, 3, 5, 4, 2, 6, 7]
6
:转换索引4
和6
→[0, 1, 3, 5, 6, 2, 4, 7]
索引7
:转换索引5
和7
→[0, 1, 3, 5, 6, 7, 4, 2]
最后删除子范围6...
答案 2 :(得分:6)
针对Swift 2.0进行了更新:
extension Array {
mutating func removeAtIndices(incs: [Int]) {
incs.sort(>).map { removeAtIndex($0) }
}
}
如果它发出不使用结果的警告,请使用forEach
代替map
(我认为自Swift 2 beta 6以来)
编辑:超级通用懒人解决方案:
extension RangeReplaceableCollectionType where Index : Comparable {
mutating func removeAtIndices<S : SequenceType where S.Generator.Element == Index>(indices: S) {
indices.sort().lazy.reverse().forEach{ removeAtIndex($0) }
}
}
答案 3 :(得分:5)
我最终这样做了:
根据Apple's documentation on NSIndexSet
,“索引将索引存储为已排序的范围”。所以我们可以向后枚举给定的NSIndexSet
并逐个从每个索引处删除数组中的元素,如下所示:
extension Array {
mutating func removeAtIndexes(indexes: NSIndexSet) {
for var i = indexes.lastIndex; i != NSNotFound; i = indexes.indexLessThanIndex(i) {
self.removeAtIndex(i)
}
}
}
答案 4 :(得分:4)
Ethan's answer上的另一个扩展(从我自己的框架中删除):
extension Array {
// Code taken from https://stackoverflow.com/a/26174259/1975001
// Further adapted to work with Swift 2.2
/// Removes objects at indexes that are in the specified `NSIndexSet`.
/// - parameter indexes: the index set containing the indexes of objects that will be removed
public mutating func removeAtIndexes(indexes: NSIndexSet) {
for i in indexes.reverse() {
self.removeAtIndex(i)
}
}
}
此版本使用Swift基金会桥提供的NSIndexSet
SequenceType
扩展名。
编辑:此外,更新版本的Swift(不记得添加了哪一个)有一个名为struct
的{{1}}类型,可以与IndexSet
桥接。并且NSIndexSet
可以reverse()
。
答案 5 :(得分:2)
要完成Ethan's answer,将在Swift 3.0中弃用C循环样式。这是一个Swift 3.0兼容的答案:
mutating func removeAtIndexes(indexes: NSIndexSet) {
var i = indexes.lastIndex
while i != NSNotFound {
self.removeAtIndex(i)
i = indexes.indexLessThanIndex(i)
}
}
答案 6 :(得分:1)
我使用了Swift的filter
函数:
func removeMusicListAtIndexes(idxSet: NSIndexSet) {
if idxSet.containsIndex(selectedMusic) {
selectedMusic = -1;
}
musicList = musicList.filter({
var idx = find(self.musicList, $0)
// If a value isn't in the index, it isn't being removed
return !idxSet.containsIndex(idx!)
})
}
答案 7 :(得分:1)
我需要这个才能使用NSTableview。这就是我使用的。
.bashrc
答案 8 :(得分:1)
基于Kent的解决方案,但针对 Swift 3
进行了更新extension Array {
mutating func remove(indices: IndexSet) {
self = self.enumerated().filter { !indices.contains($0.offset) }.map { $0.element }
}
}
答案 9 :(得分:1)
Swift 4尝试
with products as
(
select P_ID
, C_ID
from CompanyProducts
join Orders
on P_Name like '%' + O_Name + '%'
)
select c.C_ID
, (select count(distinct P_ID) from products) - count(distinct P_ID)
from Companies c
left join
products p
on c.C_ID = p.C_ID
group by
c.C_ID
答案 10 :(得分:0)
一种方法是:
var arrayB = arrayA.removeAtIndex(5)
另一种方式是:
var arr = ["I", "Love", "Life"]
let slice = arr[1..<2]
println(slice)
//[Love]
答案 11 :(得分:0)
答案 12 :(得分:0)
我还没有测试过,但是我敢打赌,如果非变异版本复制连续的子范围,它会更快。像这样:
state
答案 13 :(得分:-3)
以下是我目前使用的解决方案:
extension Array {
mutating func removeObjectAtIndexes(indexes: [Int]) {
var indexSet = NSMutableIndexSet()
for index in indexes {
indexSet.addIndex(index)
}
indexSet.enumerateIndexesWithOptions(.Reverse) {
self.removeAtIndex($0.0)
return
}
}
mutating func removeObjectAtIndexes(indexes: Int...) {
removeObjectAtIndexes(indexes)
}
}