Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array.reduce cannot assign through subscript: 'x' is a 'let' constant

Tags:

arrays

swift

I'm trying to use the reducefunction on an array:

var fields = ["gender", "name", "nickname"]
var stats = fields.reduce([String:String]()) { (res, field) -> [String:String] in
  res[field] = json[field] ?? ""
  return res
}

It's just going through the fields array and assigns values from json object to res.

Seems simple to me but the compiler keeps saying:

error: cannot assign through subscript: 'res' is a 'let' constant

on the line:

res[field] = json[field] ?? ""

It doesn't make any sense to me because I've never specified res or stats as let.

like image 286
az2902 Avatar asked Dec 05 '22 13:12

az2902


1 Answers

Closure parameters (unless declared with inout) are implicitly constants, as if declared with let. If you want to modify it then you have to make mutable copy first:

let stats = fields.reduce([String:String]()) { (res, field) -> [String:String] in
    var res = res
    res[field] = json[field] ?? ""
    return res
}

As of Swift 4 you can use reduce(into:_:):

let stats = fields.reduce(into: [String:String]()) { (res, field) in
    res[field] = json[field] ?? ""
}

Here res is an "inout" parameter and can be mutated in the closure. This is far more efficient, because no copies are made in each iteration step.

See also SE-0171 Reduce with inout.


A different Swift 4 solution would be create a new dictionary by mapping each field to a key/value pair:

let stats = Dictionary(uniqueKeysWithValues: fields.map { ($0, json[$0] ?? "") })
like image 107
Martin R Avatar answered May 25 '23 10:05

Martin R