如何调整字典的值以快速阅读?

时间:2019-04-12 13:25:57

标签: swift

编辑后的问题:如何快速调整字典的值以进行阅读?

我想调整字典的值以供阅读而不更改原点。我实现了以下课程。

class DictionaryView<Key, Value> where Key: Hashable {
   let origin: Dictionary<Key, Value>
   let map: (Value) -> Value

   init(_ origin: Dictionary<Key, Value>,
        map: @escaping (Value) -> Value) {
       self.origin = origin
       self.map = map
    }

   subscript(_ key: Key) -> Value?{
      let value = origin[key]
      return value == nil ? value : map(value!)
   }

}

但是不能按我预期的那样工作。以下代码

var origin = ["A":2, "B":3]
var adapted = DictionaryView(origin, map: { $0 * 2 })
origin["C"] = 6

print(origin)
print(adapted["A"])
print(adapted["B"])
print(adapted["C"]) 

给我输出:

  

[“ B”:3,“ C”:6,“ A”:2]
  可选(4)
  可选(6)
  无

我需要进行改编,以打印出由nil插入的Optional(12)。 怎么做?


原始问题:如何快速调整字典的值?

我有一本字典origin

var origin = ["A":2, "B":3]

并且要创建一个新字典,该字典将基于origin但包含双精度值。我朴素的解决方案是通过mapValues

对其进行映射
var adapted = origin.mapValues { $0 * 2 }

但是此解决方案基于origin状态的时刻创建了一个新的转换字典。例如以下代码

origin["C"] = 6

print(origin)
print(adapted)

输出

  

[“ A”:1,“ B”:2,“ C”:6]
  [“ A”:2,“ B”:4]

但是我想要真正的适应。我需要一个解决方案,为上面的代码输出如下结果。

  

[“ A”:1,“ B”:2,“ C”:6]
  [“ A”:2,“ B”:4,“ C”:12]

我是新手,对标准库不了解。 在开始自行车设计之前,我想问:快速解决这个问题的通用解决方案是什么?


备注1

我在实际情况中发现了问题,但是创建了一个简化的示例来提取当前问题的本质(避免细节混乱)。但据我所知,有必要解决这个现实情况。

我需要实现可观察模式,并在Observable.swift文件中开发它的4个部分。

订阅:

struct Subscription {
    private let handler: () -> ()

    init(cancel handler: @escaping () -> ()) {
        self.handler = handler
    }

    func cancel() {
        handler()
    }
}

观察者:

struct Observer<Event> {
    private let handler: (Event) -> ()

    init(send handler: @escaping (Event) -> ()) {
        self.handler = handler
    }

    func send(_ event: Event) {
        handler(event)
    }
}

可观察:

class Observable<Event> {

    fileprivate var observers: [UUID : Observer<Event>] = [:]
    func subscribe(_ observer: Observer<Event>) -> Subscription {
        let id = UUID()
        observers[id] = observer
        return Subscription(cancel: { self.observers[id] = nil })
    }
}

主题:

class Subject<Event> : Observable<Event> {
    private(set) lazy var observer = Observer<Event>(
        send: { event in
            for o in self.observers.values {
                o.send(event)
            }})
}

通常,我在客户端类外部提供了一个Observable的Subject实例,并在内部将其用作Observer进行了很好的封装。使用示例:

class Acc {
   private let numberAdded: Observer<Int> 
   let numberDidAdd: Observable<Int>

   init() {
     let subject = Subject<Int>()
     numberAdded = subject.observer
     numberDidAdd = subject
   }

   func add(_ number: Int) {
       numberAdded.send(number)
   }
}

let acc = Acc()
acc.numberDidAdd.subscribe(Observer<Int>(send: { print($0) }))
acc.add(4)
acc.add(5)

在需要简单事件的情况下,此解决方案对我很有用。当我需要实现更复杂的事件订阅接口时,问题就开始了。我需要提供一个事件字典(外面是可观察的事物,里面是观察者)。以下代码是真实上下文的一部分。描述这种上下文并不容易,但是我认为可以从代码中捕获它。

class RealClass {

    let buttonEvents = ButtonEvents()

    override func viewDidLoad() {
        super.viewDidLoad()

        for (event, observer) in buttonEvents.observers {
            someInternalSubscriptionFunc(event, observer)
        }

        buttonEvents.newEventTypeDidAdd.subscribe(
            Observer(send: { event in
                self.someInternalSubscriptionFunc(
                   event, self.buttonEvents.observers[event]!)
            })
    }

    public class ButtonEvents {

        private let newEventTypeAdding = Subject<UIControl.Event>()
        private(set) lazy var newEventTypeDidAdd
            : Observable<UIControl.Event> 
            = newEventsTypeAdding

        private var subjects: [UIControl.Event : Subject<Point>] = [:]
        fileprivate private(set) lazy var observers
            : [UIControl.Event : Observer<Point>] 
            = subjects.mapValues({ subject in subject.observer })

        public subscript(event: UIControl.Event) -> Observable<Point> {
            if subjects[event] == nil {
               subjects[event] = Subject<Point>()
               newEventTypeAdding.send(event)
            }

            return subjects[event]!
        }
    }
}

我们可以看到ButtonEvents.observers是问题的根源。

我可以将observers重新实现为计算属性,但是在字典项访问中我将失去性能O(1)。

4 个答案:

答案 0 :(得分:3)

这是因为

  

字典是值类型

您应该在这里使用Computed Property

var adapted: [String: Int] {
    return origin.mapValues { $0 * 2 }
}

现在将其命名为结果符合预期的任何地方

答案 1 :(得分:2)

根据您的最后修改:

struct AdaptedDict<Key: Hashable, Value> {

    private var origin: UnsafeMutablePointer<[Key: Value]>
    private let transform: (Value) -> Value

    init(_ origin: inout [Key: Value], transform: @escaping (Value) -> Value) {
        self.origin = UnsafeMutablePointer(&origin)
        self.transform = transform
    }

    subscript(_ key: Key) -> Value? {
        if let value = origin.pointee[key] {
            return transform(value)
        }
        return nil
    }

}

var origin = ["A": 10, "B": 20]
var adaptedDict = AdaptedDict(&origin) { $0 * 2 }
print(origin["A"], adaptedDict["A"])
origin["A"] = 20
print(origin["A"], adaptedDict["A"])

因此,基本上,您使用指针存储字典。

答案 2 :(得分:1)

Swift.Dictionary具有值语义。如果需要引用语义,则Foundation具有NSMutableDictionary。除非您有真正的性能问题,否则您真的不应该担心Swift.Dictionary(即,您正在变异数百万条记录,并且确定它实际上在写入时触发了复制)。

import PlaygroundSupport
import Foundation

let d: NSMutableDictionary = [ "a": 1, "b": 2]
for (key, value) in d {
    guard let value = value as? Int else { continue}
    d[key] = value * 2
}
print(d)

答案 3 :(得分:0)

正如 Martin R 所说,如果在生成origin数组后修改adapted数组不会影响它。因此,您首先创建origin

var origin = ["A":2, "B":3]

然后通过添加origin["C"] = 6对其进行修改,然后可以创建adapted数组。

var adapted = origin.mapValues { $0 * 2 }
print(origin)
print(adapted)

这应该给您期望的结果。