AfricanSwift AfricanSwift - 3 months ago 20
Swift Question

Swift 2D Array Generic Extension - issue accessing 2nd dimension

I'm trying to transform the following function into a generic extension for 2D Arrays.

func rotate(_ input: [[Int]]) -> [[Int]]
{
let length = input[0].count
var value = Array(repeating: [Int](), count: length)
for index in 0 ..< length
{
value[index] = input.map { $0[index] }.reversed()
}
return value
}


I'm specifically stumped as to how to specify the constraints to allow me to access the second dimension. Here's a failed attempt:

extension Array where Element: Collection, Element.Iterator.Element: Collection
{
private func rotate()
{
let count = self[0].count // Element.IndexDistance instead of Int

// Expression type 'Array<Element>' is ambiguous without more context
var returnValue = Array(repeating: Element, count: 11)
for index in 0 ..< count // Element.IndexDistance instead of Int
{
returnValue[index] = self.map { $0[index] }.reversed()
}
return returnValue
}
}

Answer

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 Ints (as the subscript expects an Index).

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'll want 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.

All in all, you'll want your method to 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

        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
    }
}