2015-09-05 121 views
2

我已经习惯了与Python的defaultdicts模式是一个字典,如果没有明确设置给定键的值,它将返回一个默认值。试图在Swift中做这个有点冗长。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 
} 

我知道我可以做一个类,这样做,但后来实际字典必须通过属性来访问使用任何其他正常字典方法

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 
     } 
    } 
} 

有没有更好的这样的模式?

+2

你可以使用nil-coalescing运算符''',就像这样:'var value:Array = dict [key] ?? []' –

+0

@TheParamagneticCroissant这将分配所需的值给变量,但不会更新词典 – alexp

+0

然后写'dict [key] = dict [key] ?? []',然后你可以强制解开'dict [key]'。 –

回答

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] 
0

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

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] 
+0

如果'Value'是'class'类型,它不起作用,因为引用类型不会将其突变传播给所有者。 – rintaro

+0

也许我不了解你。 [但是当我用一个类实现上述时,它似乎工作正常](http://swiftstub.com/229839678/?v=beta)。 – oisdk

+0

而且它基本上是一个语法问题。在python中很好,我可以说x = collections.defaultdict(list)然后做x [“foo”]。append(“bar”)而不关心x [“foo”]是否已经被初始化。每次重复默认值都不是非常方便。 – alexp

3

这改变了斯威夫特4,现在有一种方法来读取一个关键的价值,或者提供,如果默认值键不存在。例如:

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

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

此修改字典中的值时,因为你可以这样写代码是非常有用的。

+0

这很好理解,但它不完全相同。使用defaultdict-like方法,您可以声明默认值一次,这很好。使用这种方法,您看起来每次从字典中读取时都必须传递默认值。 – alexp