Mutating of array elements in a for...in loop

I currently have the following:

        for i in record[keyPath: property].startIndex ..< record[keyPath: property].endIndex {
            let (lower, upper) = (start, start + len)
            start = upper  // next start
            if let value = Value(buffer: buffer[lower ..< upper], conversionOptions: options) {
                record[keyPath: property][i] = value
            }
        }

(Hopefully understandable given lack of full context).
Is there any way to avoid using explicit subscripting yet still be able to mutate the value of the Array referred to by record[keyPath: property]?

I tried this:

        for var element in record[keyPath: property] {
            let (lower, upper) = (start, start + len)
            start = upper  // next start
            if let value = Value(buffer: buffer[lower ..< upper], conversionOptions: options) {
                element = value
            }
        }

But element is only a copy of the actual element, and thus the actual array element is not mutated. :frowning:

IIRC there was a thread that discussed a method that can mutate each element of a mutable collection, but please search for that thread on your own (I don't have time to do it).

Here is a quick function that I wrote to showcase how you could workaround your issue nicely:

var test = [0, 1]

extension MutableCollection {
  mutating func updateEach(_ update: (inout Element) -> Void) {
    for i in indices {
      update(&self[i])
    }
  }
}

test.updateEach {
  if $0 == 1 {
    $0 = 42
  }
}

print(test) // [0, 42]
1 Like

That works perfectly!

        record[keyPath: property].updateEach { element in
            let (lower, upper) = (start, start + len)
            start = upper  // next start
            if let value = Value(buffer: buffer[lower ..< upper], conversionOptions: options) {
                element = value
            }
        }

I wonder why its not in the standard library. Is it somehow considered "bad practice"? I'll see if I can find the other thread that discusses it.

updateEach is fundamentally an in-place map, which I think would be a great addition to the standard library. Whether the closure should be (inout Element) -> Void or Element -> Element is one major question. For now, you could implement this as something like:

record[keyPath: property] = record[keyPath: property].map { … }
1 Like

Found the old thread: Proposal: "inout" in for loops. From 2015. Seems like it should have made stdlib by now...

It touches on something we want to do as part of the ownership manifesto. Look for the section on for loops.

That would be built into the language (as exposed by adoption of certain standard protocols, of course).

8 Likes

Looks good. Let's do it! :-)