There's no need to limit all these ideas to the concrete Array
type.
Here's my solution. This discussion was great in that I just learned about RangeReplaceableCollection
. Merging (what I think is) the best of both worlds, I pushed all the operations down (up?) the Type hierarchy as far as possible.
Subscript works on much more than Array
as @Hamish says. But also, there's no need to constrain the index type, so we have to get rid of IndexPath
. We can always sugar this with typealias Index2d = ...
extension Collection where Self.Element: Collection {
subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
get {
return self[indexTuple.row][indexTuple.column]
}
}
}
Why not have a mutable version at the most generic possible level (between Collection
and RangeReplaceableCollection
) (unfortunately I don't think the getter can be inherited when we redefine subscript
):
extension MutableCollection where Self.Element: MutableCollection {
subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
get {
return self[indexTuple.row][indexTuple.column]
}
set {
self[indexTuple.row][indexTuple.column] = newValue
}
}
}
Then, if you want to initialize lazily, avoid using init:repeatedValue
and revise set
to have auto-initialization semantics. You can trap bounds overflow and add missing empty elements in both dimensions by integrating the accepted answer's fillingAppend
idea.
And when creating a 2D initializer, why not extend the idea of repeating
in the natural way:
extension RangeReplaceableCollection where Element: RangeReplaceableCollection {
init(repeating repeatedVal: Element.Element, extents: (row: Int, column: Int)) {
let repeatingColumn = Element(repeating: repeatedVal, count: extents.column)
self.init(repeating: repeatingColumn, count: extents.row)
}
}
Example Usage:
enum Player {
case first
case second
}
class Model {
let playerGrid: Array<Array<Player>> = {
var p = [[Player]](repeating: .first, extents: (row: 10, column: 10))
p[(3, 4)] = .second
print("Player at 3, 4 is: \(p[(row: 3, column: 4)])")
return p
}()
}