[swift-evolution] [Pitch] remove(at: Set<Index>)

Haravikk swift-evolution at haravikk.me
Sun Jun 19 05:12:07 CDT 2016


> On 19 Jun 2016, at 05:58, Saagar Jha via swift-evolution <swift-evolution at swift.org> wrote:
> 
> This isn’t actually that complex, especially if you ditch the “C-style” for loop algorithm and switch it to, as you mentioned, “filtering code”. filter, enumerated (to get indices), and map (to go back to elements) are more than up to the task. Plus, this is much more efficient.
> 
> var myArray = [0, 1, 2]
> let indices: Set = [0, 1]
> myArray = myArray.enumerated().filter {
>     return !indices.contains($0.offset)
> }.map {
>     return $0.element // to get the elements back
> }
> print(myArray) // prints “[2]"
> Adding it to the standard library might be useful, but it’s not as hard as it looks.
> 

Has .enumerated() been changed to use the collection’s internal Index type? Last time I tried something like this it failed because .enumerated() only returns numeric offsets, but the index type of a collection may not necessary be compatible (not all indices are just a plain numeric value, consider a type that returns AnyIndex). So this will only work when your indices are integers starting from zero.

I think the following should work for the generic case, and is pretty similar:

extension MutableCollection {
	public mutating func remove(indices: Set<Index>) {
		var index = self.startIndex
		self = self.filter {
			let result = indices.contains(index)
			self.formIndex(after: &index)
			return result
		}
	}
}

(note: I'm not in a position to test this just now so it may not work exactly as written, but that’s the gist of how to do it safely I think)

The main question I have is how do you get into a situation where you’ve generated the indices, but could not put the same code into a call to .filter? For example:

	var indicesToRemove:Set<Index> = []
	for eachIndex in myArray.indices {
		if someCondition(myArray[eachIndex]) { indicesToRemove.insert(eachIndex) }
	}
	myArray.remove(indices: indicesToRemove)

Could be rewritten as:

	myArray = myArray.filter { someCondition($0) }

If what we want is in-place removal then I think what we need is some kind of MutatingIteratorProtocol which includes a .remove() method; this method would remove the last element retrieved without invalidating the iterator or skipping an element, allowing us to do the following:

	var iterator = myArray.makeIterator() // Conforms to MutatingIteratorProtocol
	while let eachElement = iterator.next() {
		if someCondition(eachElement) { iterator.remove() }
	}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160619/56d817c1/attachment.html>


More information about the swift-evolution mailing list