The docs for reversed()
specifically call out its behavior and how to get the functionality you want. Is there something else you would expect in the documentation?
Furthermore, copying map
and filter
's precedent and creating a fresh array would make the interface less useful because not only does it not provide the big win of shared indices, it brings a risk of switching the index to be a type that can be reused as far as the type system is concerned but is definitely not shared, a likely cause of bugs. And it makes the task of going to the same location in the base collection even harder.
My case is fairly simple: I have an array of placemarks that I store in one order (to make insertions efficient) and display in a table in reverse order. I was going to use ReverseCollection
for the latter, so I could do placemarks.reversed()[indexPath.row]
. Of course, though, I can write [placemarks.count - 1 - indexPath.row]
or extend Array
with a new subscript that reverses (mirrors) the given index. And that is a better solution for this case, since you avoid creating reverse wrappers to begin with. I just thought this is only one of many possible scenarios where one would want to subscript a reverse collection.
That makes sense! For something that direct, I think I might add a convenience fromEnd(_:)
method to make it easier.
As your needs grow more complex, however, that might be insufficient — if you need to subset or page your data in certain contexts, you might not be offsetting from the end, or might be using an ArraySlice
instead of Array
. This is where the consistency of Swift's collection protocols really starts to be beneficial.
Imagine that you've built a super-simple generic table view data source that is generic over a collection:
class TVDS<C: Collection>: NSObject, UITableViewDataSource {
var data: C
var cellBuilder: (C.Element) -> UITableViewCell
func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let i = data.index(data.startIndex, offsetBy: indexPath.row)
return cellBuilder(data[i])
}
}
Inside the TVDS
implementation, you're working with Collection
methods, not anything Array
- or Int
-specific. For that reason, it doesn't matter to TVDS
what kind of collection you pass:
TVDS(data: myData) { /* ... */ }
TVDS(data: myData.reversed()) { /* ... */ }
TVDS(data: myData.reversed().prefix(10)) { /* ... */ }
TVDS(data: myData.reversed().dropFirst(30).prefix(10)) { /* ... */ }
For what it's worth, I really like the way ReversedCollection
and others are designed. I wrote a blog post about this where I used a potential implementation for lastIndex(of:)
as an example:
extension String {
func lastIndex2(of char: Character) -> String.Index? {
guard let reversedIndex = reversed().firstIndex(of: char) else {
return nil
}
return index(before: reversedIndex.base)
}
}
(This in on String
to make the example a bit simpler, but it could be on BidirectionalCollection
.)