Reputation: 272895
Using Set
s, I can conveniently insert an element into the set, and also check whether the element was in the set using one call to insert
:
let array = [1,2,3,4,4,2,5,3,6,7,1]
var set = Set<Int>()
for item in array {
// set.insert not only inserts the item, but also tells me whether the item was in set before the insert
if set.insert(item).inserted {
print("Encountered new item: \(item)")
} else {
print("\(item) has already been encountered!")
}
}
Output:
Encountered new item: 1
Encountered new item: 2
Encountered new item: 3
Encountered new item: 4
4 has already been encountered!
2 has already been encountered!
Encountered new item: 5
3 has already been encountered!
Encountered new item: 6
Encountered new item: 7
1 has already been encountered!
However, if I rewrite the the same logic using IndexSet
:
let array = [1,2,3,4,4,2,5,3,6,7,1]
var set = IndexSet()
for item in array {
// set.insert not only inserts the item, but also tells me whether the item was in set before the insert
if set.insert(item).inserted {
print("Encountered new item: \(item)")
} else {
print("\(item) has already been encountered!")
}
}
The output becomes:
Encountered new item: 1
Encountered new item: 2
Encountered new item: 3
Encountered new item: 4
Encountered new item: 4
Encountered new item: 2
Encountered new item: 5
Encountered new item: 3
Encountered new item: 6
Encountered new item: 7
Encountered new item: 1
It appears that the tuple returned by IndexSet.insert
always matches (true, _)
, but even so, the set's count
is not increased. I must check set.contains(item)
before insert
to produce the desired output.
Question: Is this intended behaviour for IndexSet
?
I know that IndexSet
is a Cocoa API, and not native to Swift, so I thought perhaps there is some special semantics that IndexSet
has about what it means for two numbers to be "the same" that I'm not aware of. I also looked for bug reports on bugs.swift.org about IndexSet
, but I didn't find anything about insert
always returning true.
Upvotes: 3
Views: 177
Reputation: 539965
IndexSet
is a the Swift value overlay type for NSIndexSet
and NSMutableIndexSet
. As can be seen in the implementation in IndexSet.swift, all operations on the IndexSet
are forwarded to NS(Mutable)IndexSet
.
// Temporary boxing function, until we can get a native Swift type for NSIndexSet
@inline(__always)
mutating func _applyMutation<ReturnType>(_ whatToDo : (NSMutableIndexSet) throws -> ReturnType) rethrows -> ReturnType {
// ...
}
Apparently that does not allow to determine if an inserted already exists, this is marked as “TODO” in the source code:
/// Insert an integer into the `IndexSet`.
@discardableResult
public mutating func insert(_ integer: Element) -> (inserted: Bool, memberAfterInsert: Element) {
_applyMutation { $0.add(integer) }
// TODO: figure out how to return the truth here
return (true, integer)
}
So IndexSet.insert(value)
calls NSMutableIndexSet.add(value)
, which adds the new value to the index set if it is not present. It behaves correctly in so far as the indices are unique, but the inserted
return value is always true
.
Upvotes: 3