Hi all, a few years ago @nnnnnnnn proposed new additions to the standard library to describe collection operations over noncontiguous elements with a new type named RangeSet
. While the final revision of the API design was approved, its implementation was reverted before release. I'd like to re-pitch this idea for inclusion in the standard library with a few modifications to the API surface to tweak a few issues and modernize the API. In particular, I'd like to highlight the following changes from the previously approved proposal:
- The
subranges(where:)
/subranges(of:)
APIs were renamed toindices(where:)
/indices(of:)
to avoid conflicting with recently introduced APIs related to the string processing module - I've added some missing conformances to
Equatable
/Hashable
/Sendable
where appropriate - I've removed
DiscontiguousSlice
'sMutableCollection
conformance since this API is slightly misleading as it did not guarantee an in-place mutation and it is also hard to use and implement with predictable behavior. Rather than leading developers down the wrong path, I've chosen to remove the conformance.
Take a look below and let me know if you have any comments on the revived proposal!
Add Collection Operations on Noncontiguous Elements
- Proposal: SE-0270
- Authors: Nate Cook, Jeremy Schonfeld
- Review Manager: TBD
- Status: Previewing
- Implementation: apple/swift#69766
- Previous Revisions: 1, 2, 3
- Reviews: 1, 2, 3
- Decision Notes: 1, 2, 3
Introduction
We can use a Range<Index>
to refer to a group of consecutive positions in a collection, but the standard library doesn't currently provide a way to refer to discontiguous positions in an arbitrary collection. I propose the addition of a RangeSet
type that can represent any number of positions, along with collection algorithms that operate on those positions.
Motivation
There are varied uses for tracking multiple elements in a collection, such as maintaining the selection in a list of items, or refining a filter or search result set after getting more input from a user.
The Foundation data type most suited for this purpose, IndexSet
, uses integers only, which limits its usefulness to arrays and other random-access collection types. The standard library is missing a collection that can efficiently store ranges of indices, and is missing the operations that you might want to perform with such a collection. These operations themselves can be challenging to implement correctly, and have performance traps as well — see WWDC 2018's Embracing Algorithms talk for a demonstration.
Proposed solution
This proposal adds a RangeSet
type for representing multiple, noncontiguous ranges, as well as a variety of collection operations for creating and working with range sets.
var numbers = Array(1...15)
// Find the indices of all the even numbers
let indicesOfEvens = numbers.indices(where: { $0.isMultiple(of: 2) })
// Perform an operation with just the even numbers
let sumOfEvens = numbers[indicesOfEvens].reduce(0, +)
// sumOfEvens == 56
// You can gather the even numbers at the beginning
let rangeOfEvens = numbers.moveSubranges(indicesOfEvens, to: numbers.startIndex)
// numbers == [2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15]
// numbers[rangeOfEvens] == [2, 4, 6, 8, 10, 12, 14]
I've posted the full proposal including the detailed design and additional sections as a gist here.