I\'m trying to transform the following function into a generic extension for 2D Arrays.
func rotate(_ input: [[Int]]) -> [[Int]]
{
let length = input[0]
The problem is that the compiler doesn't know that your extension is for 2D arrays – it just knows that it's for arrays of collections. Therefore the associated types IndexDistance
and Index
aren't necessarily Int
.
The solution therefore is to constrain your extension so that the Element
's IndexDistance
and Index
are of type Int
. This will let you form the range 0..<count
, as count
will now be of type Int
(IndexDistance
) – and you will be able to subscript the elements within the map(_:)
with Int
s (as the subscript expects an Index
).
(This will be trivial to do once concrete same-type requirements are supported, as you could simply constrain the Element
to be an Array
, however this is not yet possible.)
You should also note that your constraint Element.Iterator.Element: Collection
is incorrect, as that'll constrain the extension to 3D arrays of collections (An array where the element is a collection, where the elements of that collection are a collection).
Finally, you may have to define a typealias
to represent the 'inner element' type of the 2D array, as Swift currently has some limitations when working with nested types directly, for example when creating an empty 2D array of that type.
Therefore, a working version of your current method would look something like this:
extension Array where Element: Collection, Element.Index == Int, Element.IndexDistance == Int {
private func rotate() -> [[Element.Iterator.Element]] {
typealias InnerElement = Element.Iterator.Element
// in the case of an empty array, simply return an empty array
if self.isEmpty { return [] }
let length = self[0].count
var returnValue = [[InnerElement]](repeating: [InnerElement](), count: length)
for index in 0..<length {
returnValue[index] = self.map{ $0[index] }.reversed()
}
return returnValue
}
}
Which, as @MartinR points out below, could be simplified considerably by using a nested map(_:)
, eliminating the need of a typealias
as we no longer need to create a 'result' array:
private func rotate() -> [[Element.Iterator.Element]] {
if self.isEmpty { return [] }
let length = self[0].count
return (0..<length).map { index in
self.map { $0[index] }.reversed()
}
}
Although note that the constraining of your extension to only work with Int
indices isn't strictly necessary (however makes no practical difference as you're only intending to use this with 2D arrays). Another alternative is to just iterate directly over the inner collection's indices
.
In order to do this, you simply have to constrain your extension so that the inner collection's Indices
have Element
s of the same type as the Index
of the collection (in the case of an Array
, Indices
is a CountableRange<Int>
):
extension Array where Element: Collection, Element.Indices.Iterator.Element == Element.Index {
private func rotate() -> [[Element.Iterator.Element]] {
if self.isEmpty { return [] }
return self[0].indices.map { index in
self.map { $0[index] }.reversed()
}
}
}