1

I'd like to make a slick initializer for a matrix that lets me define the row and column count with a default element. How can create an extension for an Array of Arrays? When I try in the way below,

extension Array where Element == Array<SubElement> {

    init(rows: Int, columns: Int, emptyDefault: SubElement) {

        self = [] 
        // implementation
    }

}

I'm getting the following error:

// Same-type constraint 'Element' == 'Array<Element>' is recursive

An example usage would be

self = [[UIColor]](rows: 20, columns: 30, emptyDefault: .blue)
// This would create a matrix with 20 rows, each row having an array of 30 .blue

I want to be able to do something like that.

3 Answers 3

3

Leo's answer is good. 🏆

But the more constricted solution looks like this, moving the constraint to the initializer:

extension Array {
  init<Element>(rows: Int, columns: Int, emptyDefault: Element) where Self.Element == [Element] {

  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

ooh clever. I like it.
I liked it too until you made that line too long! where clauses generally make lines too wide, so I line up where with the keyword that starts whatever it's constricting. Also, I purposefully left off the closing braces. Like this, it won't compile!
I'm sorry! I just saw this. Did you change it back?
2

If you need to create a matrix of elements you can extend RangeReplaceableCollection and constrain its elements to RangeReplaceableCollection as well. You would need to add default element of type Element.Element to your initializer to fill your collections:

extension RangeReplaceableCollection where Element: RangeReplaceableCollection { 
    init(rows: Int, columns: Int, element: Element.Element) { 
        self.init(repeating: .init(repeating: element, count: columns), count: rows) 
    }
}

Usage:

let matrix: [[UIColor]] = .init(rows: 3, columns: 3, element: .blue)

10 Comments

Can you include that empty init() implementation in the answer too? I was sad to see that deleted.
You can just to pass the init as the element. The other approach you would need to make the types conform to the protocol
As I said you just need to pass the init as the element let matrix: [[String]] = .init(rows: 3, columns: 3, element: .init())
protocol HasInit { init() } extension String: HasInit {} extension RangeReplaceableCollection where Element: RangeReplaceableCollection, Element.Element: HasInit { init(rows: Int, columns: Int) { self.init(repeating: .init(repeating: Element.Element(), count: columns), count: rows) } }
IMO passing the init() is preferred.
|
0

Its not perfect, Here my temporary work around:

extension Collection where Element: Collection {

}

7 Comments

Good call. It's weird that Swift permits recursive conformance but not recursive equality.
@LeoDabus, updated. I want to add methods to generic Array of Arrays which can take a generic element at each cell.
Its pretty simple, I just want to initialize a matrix with empty cells while defining the number of rows and number of columns. I can add that.. I don't know how to make it more specific.
[ [/*row1*/ "cell 1", "cell 2" ], [/*row2*/ "cell 3", "cell 4" ], [/*row3*/ "cell 5", "cell 6" ] ]
Wow. Extremely clever. Thank you. If you feel like posting that as an answer, that is exactly what I'm looking for.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.