Search code examples
swift3enumerationnscountedset

NSCountedSet enumeration in Swift


I'm looking for a way to use NSCountedSet in a more Swift-like way (whatever that means).

Consider the following snippet that I basically translated directly from Objective C. I iterate over each symbol (a String) in the set, get its corresponding count, and look up a value for that symbol in a dictionary. Then I multiply that value with the count and add it to the total.

var total = 0

for symbol in symbolSet {
    let count = symbolSet.count(for: symbol)
    let item = symbolDictionary[symbol as! String]
    let value = item?.value

    total+= (count * value!)
}

It works, but I am a bit concerned about the unwrapping that Xcode suggested for me. So I am trying to do this a more Swift like way so that it is more safe without all the unwrapping.

I started with something like this:

symbolSet.enumerated().map { item, count in
    print(item)
    print(count)
}

But count here is not the actual count, but an enumeration index.

How do I move forward with this?


Solution

  • You could chain a flatMap followed by a reduce operation on your symbolSet,

    • The flatMap operation applies attempted conversion of the symbolSet members to String
    • The following reduce operation computes the weighted sum of the count of the symbols in the symbolSet (for the symbols that was successfully converted to String instances)

    Example setup:

    struct Item {
        let value: Int
        init(_ value: Int) { self.value = value }
    }
    
    let symbolDictionary = [
        "+" : Item(1),
        "-" : Item(2),
        "/" : Item(4),
        "*" : Item(8)
    ]
    
    var symbolSet = NSCountedSet()
    symbolSet.add("*") // accumulated: 8
    symbolSet.add("/") // accumulated: 8 + 4 = 12
    symbolSet.add("+") // accumulated: 12 + 1 = 13
    symbolSet.add("-") // accumulated: 13 + 2 = 15
    symbolSet.add("+") // accumulated: 15 + 1 = 16
    

    Compute weighted accumulated sum with the chained flatMap and reduce operations (expected result: 16):

    let total = symbolSet
        .flatMap { $0 as? String } /* <- attempted conversion of each symbol to 'String'         */
        .reduce(0) { $0 + symbolSet.count(for: $1) * (symbolDictionary[$1]?.value ?? 0) }
                   /* |   ^^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                      |              |               If a key exists for the given symbol as a
                      |              |               String, extract the 'value' property of the
                      |              |               'Item' value for this key, otherwise '0'.
                      |              |
                      |   Multiply '...value' or '0' with the 'count' for the given symbol.
                      \
                       Add the product to the accumulated sum of the reduce operation.           */
    
    print(total) // 16, ok