0

With arrays you can use a subscript to access Array Elements directly. You can read or write to them. With Sets I am not sure of a way to write its Elements.

For example, if I access a set element matching a condition I'm only able to read the element. It is passed by copy and I can't therefore write to the original.

For example:


columns.first(
    where: { 
        $0.header.last == Character(String(i))
    }
)?.cells.append(value: addValue)


// ERROR: Cannot use mutating member on immutable value: function call returns immutable value
ScottyBlades
  • 12,189
  • 5
  • 77
  • 85

2 Answers2

3

You can't just change things inside a set, because of how a (hash) set works. Changing them would possibly change their hash value, making the set into an invalid state.

Therefore, you would have to take the thing you want to change out of the set, change it, then put it back.

if var thing = columns.first(
    where: { 
        $0.header.last == Character(String(i))
    }) {
    columns.remove(thing)
    thing.cells.append(value: addValue)
    columns.insert(thing)
}

If the == operator on Column doesn't care about cells (i.e. adding cells to a column doesn't suddenly make two originally equal columns unequal and vice versa), then you could use update instead:

if var thing = columns.first(
    where: { 
        $0.header.last == Character(String(i))
    }) {
    thing.cells.append(value: addValue)
    columns.update(thing)
}

As you can see, it's quite a lot of work, so maybe sets aren't a suitable data structure to use in this situation. Have you considered using an array instead? :)

private var _columns: [Column]
public var columns : [Column] {
    get { _columns }
    set { _columns = Array(Set(newValue)) }
    // or any other way to remove duplicate as described here: https://stackoverflow.com/questions/25738817/removing-duplicate-elements-from-an-array-in-swift
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • I am using an array but I wanted to enforce no repeated elements, and I don't care about the order. – ScottyBlades Aug 16 '20 at 07:59
  • 1
    @ScottyBlades That's fine, just convert the array to a set to remove duplicates, then convert it back to an array. – Sweeper Aug 16 '20 at 08:00
  • Thats one way... I guess I was just hoping to strengthen the contract to the point where I can trust with certainty that any time this collection is used, there are no repeats. I guess its not possible with hash sets. – ScottyBlades Aug 16 '20 at 08:02
  • if Collumn is Equatable and it doesn't rely on cells property to differentiate between the set elements you can use update instead of removing and inserting. – Leo Dabus Aug 16 '20 at 08:58
  • Column is equatable based on its header property (not its cells). Not picturing how to apply update to this problem yet. Can you elaborate? – ScottyBlades Aug 16 '20 at 11:49
  • @ScottyBlades I edited the answer an hour ago to show exactly that. – Sweeper Aug 16 '20 at 11:50
  • Would the inner arrays would go in a custom struct? I could encapsulate the update code into a set extension... I think the downside to that would be the time complexity would increase relative to appending directly like I would for a class. The downside to a class is the read write/deadlock increased risk. – ScottyBlades Aug 16 '20 at 12:25
  • Maybe the inner array would go on a custom struct called UniqueArray or something... I think that would do it. I think I get it now. – ScottyBlades Aug 16 '20 at 12:28
  • 1
    @ScottyBlades What do you mean by the "inner arrays"? If you mean `_columns`, that could go in a property wrapper. And yes, the update code could go in a set extension. You can add a subscript that takes an `(inout Element) -> Void`. – Sweeper Aug 16 '20 at 12:28
  • @ScottyBlades https://stackoverflow.com/questions/59887561/how-to-implement-a-mutable-ordered-set-generic-type-formerly-known-as-nsmutableo – Leo Dabus Aug 16 '20 at 15:13
1

You are getting the error because columns might be a set of struct. So columns.first will give you an immutable value. If you were to use a class, you will get a mutable result from columns.first and your code will work as expected. Otherwise, you will have to do as explained by @Sweeper in his answer.

Jithin
  • 913
  • 5
  • 6