2

I am trying to create a Matrix class in Swift but I am getting an error on the self.data[row * columns..<(row + 1) * columns] = data line in my setRow() function. The error is 'Cannot assign value of type '[Double]' to type 'ArraySlice''

struct Matrix: CustomStringConvertible {
    let rows:Int
    let columns:Int
    var data:[Double]

    // Description
    public var description: String {
        var description = ""
        for r in 0..<rows {
            description += data[r * columns..<(r + 1) * columns].description + "\n"
        }
        return description
    }

    // Initialisation
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        self.data = Array(repeating: 0.0, count: rows * columns)
    }

    init(rows: Int, columns: Int, data:[Double]) {
        assert(data.count == (rows * columns),"Number of elements must equal rows * columns")
        self.rows = rows
        self.columns = columns
        self.data = data
    }

    // Validity
    func validRow(row: Int) -> Bool {
        return row > 0 && row < rows
    }

    func validColumn(column: Int) -> Bool {
        return column > 0 && column < columns
    }

    func validIndex(row: Int, column: Int) -> Bool {
        return validRow(row: row) && validColumn(column: column)
    }

    // Setters and getters
    func get(row: Int, column: Int) -> Double {
        assert(validIndex(row: row,column: column), "Index out of range")
        return data[(row * columns) + column]
    }

    mutating func set(row: Int, column: Int, value: Double) {
        assert(validIndex(row: row,column: column), "Index out of range")
        data[(row * columns) + column] = value
    }

    func getRow(row: Int) -> [Double] {
        assert(validRow(row: row), "Index out of range")
        return Array(data[row * columns..<(row + 1) * columns])
    }

    mutating func setRow(row: Int, data:[Double]) {
        assert(validRow(row: row), "Index out of range")
        assert(data.count == columns, "Data must be same length ans the number of columns")
        self.data[row * columns..<(row + 1) * columns] = data
    }

    // Swapping
    mutating func swapRow(row1: Int, row2: Int) {
        assert(validRow(row: row1) && validRow(row: row2), "Index out of range")
        let holder = getRow(row: row2)
        setRow(row: row2, data: getRow(row: row1))
        setRow(row: row1, data: holder)
    }
}

2 Answers 2

3

As the error says, Array's ranged subscript deals with ArraySlice, not Array.

One solution, as @vacawama says, is to just create a slice of the entire input array. This can be done by subscripting with the array's indices:

mutating func setRow(row: Int, data newData: [Double]) {
    assert(validRow(row: row), "Index out of range")
    assert(data.count == columns, "Data must be same length ans the number of columns")
    data[row * columns ..< (row + 1) * columns] = newData[newData.indices]
}

Or in Swift 4, you can take advantage of the ... 'operator', which does the exact same thing:

data[row * columns ..< (row + 1) * columns] = newData[...]

But IMO, a better tool for the job here would be replaceSubrange(_:with:):

mutating func replaceSubrange<C>(_ subrange: Range<Int>, with newElements: C) 
     where Element == C.Element, C : Collection

as it allows you to deal with an arbitrary Collection of new elements, meaning that you could also make setRow generic:

mutating func setRow<C : Collection>(row: Int, data newData: C)
    where C.Iterator.Element == Double { // <- in Swift 4, remove ".Iterator"

    assert(validRow(row: row), "Index out of range")
    assert(data.count == columns, "Data must be same length ans the number of columns")
    data.replaceSubrange(row * columns ..< (row + 1) * columns, with: newData)
}
Sign up to request clarification or add additional context in comments.

6 Comments

Why does this work? var a = [1, 2, 3, 4, 5]; a[1..<3] = [6, 7].
@vacawama Because ArraySlice is ExpressibleByArrayLiteral :) So the array literal [6, 7] is inferred to be an ArraySlice<Int>.
Very helpful, I was also wondering about why the above comment is valid, thank you.
In that case, couldn't he just do self.data[row * columns..<(row + 1) * columns] = data[0..<data.count].
@vacawama Yup, he could, or even in Swift 4 = data[...] – but IMO replaceSubrange is better suited for the task, as it lets you deal with arbitrary collections of new elements. I'll also edit in your suggestion though (unless you want to post it as an answer)
|
0

Sounds simple. Didn't you want to set just a Double instead of [Double] in your function?

mutating func setRow(row: Int, data: Double) {
        assert(validRow(row: row), "Index out of range")
        assert(data.count == columns, "Data must be same length ans the number of columns")
        self.data[row * columns..<(row + 1) * columns] = data
    }

1 Comment

No, I want to set an array of values within my self.data array

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.