Swift Dictionary的默认值

时间:2015-09-05 16:16:33

标签: python swift dictionary

我已经习惯使用Python的默认模式的模式是一个字典,如果未明确设置给定键的值,则返回默认值。试图在Swift中这样做有点冗长。

var dict = Dictionary<String, Array<Int>>()
let key = "foo"
var value: Array<Int>! = dict[key]
if value == nil {
    value = Array<Int>()
    dict[key] = value
}

我意识到我可以创建一个这样做的类,但是实际的Dictionary必须通过属性访问才能使用任何其他普通的Dictionary方法

class DefaultDictionary<A: Hashable, B> {
    let defaultFunc: () -> B
    var dict = Dictionary<A, B>()

    init(defaultFunc: () -> B) {
        self.defaultFunc = defaultFunc
    }
    subscript(key: A) -> B {
        get {
            var value: B! = dict[key]
            if value == nil {
                value = defaultFunc()
                dict[key] = value
            }
            return value
        }
        set {
            dict[key] = newValue
        }
    }
}

这有更好的模式吗?

3 个答案:

答案 0 :(得分:10)

这在Swift 4中有所改变,现在有一种方法可以读取键的值,或者如果键不存在则提供默认值。例如:

let person = ["name": "Taylor", "city": "Nashville"]
let name = person["name", default: "Anonymous"]

这在修改字典值时特别有用,因为您可以编写如下代码:

var favoriteTVShows = ["Red Dwarf", "Blackadder", "Fawlty Towers", "Red Dwarf"]
var favoriteCounts = [String: Int]()

for show in favoriteTVShows {
    favoriteCounts[show, default: 0] += 1
}

我在文章What's new in Swift 4中介绍了此更改及其他内容。

答案 1 :(得分:7)

使用Swift 2可以实现类似于python版本的扩展名Dictionary

// Values which can provide a default instance
protocol Initializable {
    init()
}

extension Dictionary where Value: Initializable {
    // using key as external name to make it unambiguous from the standard subscript
    subscript(key key: Key) -> Value {
        mutating get { return self[key, or: Value()] }
        set { self[key] = newValue }
    }
}

// this can also be used in Swift 1.x
extension Dictionary {
    subscript(key: Key, or def: Value) -> Value {
        mutating get {
            return self[key] ?? {
                // assign default value if self[key] is nil
                self[key] = def
                return def
            }()
        }
        set { self[key] = newValue }
    }
}

??之后的闭包被用于类,因为它们不传播它们的值变异(只有“指针变异”;引用类型)。

字典必须是可变的(var)才能使用这些下标:

// Make Int Initializable. Int() == 0
extension Int: Initializable {}

var dict = [Int: Int]()
dict[1, or: 0]++
dict[key: 2]++

// if Value is not Initializable
var dict = [Int: Double]()
dict[1, or: 0.0]

答案 2 :(得分:0)

除非我误解了Python中的defaultdict,否则我不知道nil合并如何对你不起作用。假设您有一个类型为[Int:Int]的字典,并且您希望它默认返回0。使用nil合并它看起来像这样:

let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4] ?? 0

您在评论中提到,这不起作用,因为它不会更新字典。但是我不明白这个问题:如果你知道nil的每个实例都会被你的默认值替换,为什么还需要更新字典呢?也许我在这里遗漏了一些东西,但似乎默认值和零合并(实际上)是相同的。

You can change the syntax a little, if it makes things more clear

extension Dictionary {
  subscript(key: Key, or r: Value) -> Value {
    get { return self[key] ?? r }
    set { self[key] = newValue }
  }
}

在这种情况下,上面的示例可以这样写:

let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4, or: 0]

在这种情况下,变异方法可以对键起作用,如下所示:

var dict = [2: 8, 3: 64, 1: 10]
dict[2, or: 0]++
dict // [2: 9, 3: 64, 1: 10]
dict[4, or: 0]++
dict // [2: 9, 3: 64, 1: 10, 4: 1]