swiftcustom-operator

Swift : Custom operator to update dictionary value


Is there an elegant way to make a custom operator that updates a dictionary value?

More specifically, I want a prefix operator that increments the integer value corresponding to a given key:

prefix operator +> {}

prefix func +> //Signature 
{
    ...
}

var d = ["first" : 10 , "second" : 33]
+>d["second"] // should update d to ["first" : 10 , "second" : 34]

This is feasible using the functional way. For example, to calculate the frequencies of elements in an array:

func update<K,V>(var dictionary: [K:V], key: K, value: V) -> [K:V] {
    dictionary[key] = value
    return dictionary
}

func increment<T>(dictionary: [T:Int], key: T) -> [T:Int] {
    return update(dictionary, key: key, value: dictionary[key].map{$0 + 1} ?? 1)
}

func histogram<T>( s: [T]) -> [T:Int] {
    return s.reduce([T:Int](), combine: increment)
}

let foo = histogram([1,4,3,1,4,1,1,2,3]) // [2: 1, 3: 2, 1: 4, 4: 2]

But I am trying to do the same thing using a custom operator


Solution

  • var d = ["first" : 10 , "second" : 33]
    
    d["second"]?++
    

    The operator could be implemented like this:

    prefix operator +> {}
    prefix func +> <I : ForwardIndexType>(inout i: I?) {
      i?._successorInPlace()
    }
    
    var dict = ["a":1, "b":2]
    
    +>dict["b"]
    
    dict // ["b": 3, "a": 1]
    

    Although I'm not sure how it would give you a frequencies function - I mean, if it's building a dictionary, it's not going to have any keys to begin with, so there won't be anything to increment. There are a bunch of cool ways to do it, though. Using the postfix ++, you can do this:

    extension SequenceType where Generator.Element : Hashable {
      func frequencies() -> [Generator.Element:Int] {
        var result: [Generator.Element:Int] = [:]
        for element in self {
          result[element]?++ ?? {result.updateValue(1, forKey: element)}()
        }
        return result
      }
    }
    

    Airspeed Velocity tweeted another cool way:

    extension Dictionary {
      subscript(key: Key, or or: Value) -> Value {
        get { return self[key] ?? or }
        set { self[key] = newValue }
      }
    }
    
    extension SequenceType where Generator.Element : Hashable {
      func frequencies() -> [Generator.Element:Int] {
        var result: [Generator.Element:Int] = [:]
        for element in self { ++result[element, or: 0] }
        return result
      }
    }
    

    Or, using an undocumented function:

    extension SequenceType where Generator.Element : Hashable {
      func frequencies() -> [Generator.Element:Int] {
        var result: [Generator.Element:Int] = [:]
        for el in self {result[el]?._successorInPlace() ?? {result[el] = 1}()}
        return result
      }
    }