I want to initialize a dictionary with a dictionary nested inside like this:
var a = [Int:[Int:Float]]()
a[1][2] = 12
But I get an error:
(Int:[Int:Float]) does not have a member named 'subscript'
I've hacked at a variety of other approaches, all of them running into some kind of issue.
Any idea why this doesn't work?
You can create your own 2D dictionary like this:
struct Dict2D<X:Hashable,Y:Hashable,V> {
var values = [X:[Y:V]]()
subscript (x:X, y:Y)->V? {
get { return values[x]?[y] }
set {
if values[x] == nil {
values[x] = [Y:V]()
}
values[x]![y] = newValue
}
}
}
var a = Dict2D<Int,Int,Float>()
a[1,2] = 12
println(a[1,2]) // Optional(12.0)
println(a[0,2]) // nil
The point is you access the element via a[x,y] instead of a[x][y] or a[x]?[y].
It's giving you that error because your first subscript returns an optional so it may return a dictionary or nil. In the case that it returns nil the second subscript would be invalid. You can force it to unwrap the optional value by using an exlamation point.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]![2]
If you aren't positive that a[1] is non-nil you may want to safely unwrap with a question mark instead.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]?[2]
You can also assign using this method. (As of Beta 5)
var a = [Int:[Int:Float]]()
a[1] = [Int: Float]()
a[1]?[2] = 12.0
a[1]?[2] //12.0
Another way to do it is with an extension to the standard dictionary:
extension Dictionary {
mutating func updateValueForKey(key: Key, updater: ((previousValue: Value?) -> Value)) {
let previousValue = self[key]
self[key] = updater(previousValue: previousValue)
}
}
Example:
var a = [Int:[Int:Float]]()
a.updateValueForKey(1) { nestedDict in
var nestedDict = nestedDict ?? [Int:Float]()
nestedDict[2] = 12
return nestedDict
}
Related
I've noticed a common pattern in Swift is
var x:[String:[Thing]] = [:]
so, when you want to "add an item to one of the arrays", you can not just
x[which].append(t)
you have to
if x.index(forKey: which) == nil {
x[which] = []
}
x[which]!.append(s!)
Really, is there a swiftier way to say something like
x[index?!?!].append??(s?!)
While this is a question about style, performance seems to be a critical issue when touching arrays in Swift, due to the copy-wise nature of Swift.
(Please note, obviously you can use an extension for this; it's a question about Swiftiness.)
Swift 4 update:
As of Swift 4, dictionaries have a subscript(_:default:) method, so that
dict[key, default: []].append(newElement)
appends to the already present array, or to an empty array. Example:
var dict: [String: [Int]] = [:]
print(dict["foo"]) // nil
dict["foo", default: []].append(1)
print(dict["foo"]) // Optional([1])
dict["foo", default: []].append(2)
print(dict["foo"]) // Optional([1, 2])
As of Swift 4.1 (currently in beta) this is also fast,
compare Hamish's comment here.
Previous answer for Swift <= 3: There is – as far as I know – no way to "create or update" a dictionary
value with a single subscript call.
In addition to what you wrote, you can use the nil-coalescing operator
dict[key] = (dict[key] ?? []) + [elem]
or optional chaining (which returns nil if the append operation
could not be performed):
if dict[key]?.append(elem) == nil {
dict[key] = [elem]
}
As mentioned in SE-0154 Provide Custom Collections for Dictionary Keys and Values and also by #Hamish in the comments, both methods
make a copy of the array.
With the implementation of SE-0154 you will be able to mutate
a dictionary value without making a copy:
if let i = dict.index(forKey: key) {
dict.values[i].append(elem)
} else {
dict[key] = [key]
}
At present, the most efficient solution is given by Rob Napier
in Dictionary in Swift with Mutable Array as value is performing very slow? How to optimize or construct properly?:
var array = dict.removeValue(forKey: key) ?? []
array.append(elem)
dict[key] = array
A simple benchmark confirms that "Rob's method" is the fastest:
let numKeys = 1000
let numElements = 1000
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
if dict.index(forKey: key) == nil {
dict[key] = []
}
dict[key]!.append(elem)
}
}
let end = Date()
print("Your method:", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
dict[key] = (dict[key] ?? []) + [elem]
}
}
let end = Date()
print("Nil coalescing:", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
if dict[key]?.append(elem) == nil {
dict[key] = [elem]
}
}
}
let end = Date()
print("Optional chaining", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
var array = dict.removeValue(forKey: key) ?? []
array.append(elem)
dict[key] = array
}
}
let end = Date()
print("Remove and add:", end.timeIntervalSince(start))
}
Results (on a 1.2 GHz Intel Core m5 MacBook) for 1000 keys/1000 elements:
Your method: 0.470084965229034
Nil coalescing: 0.460215032100677
Optional chaining 0.397282958030701
Remove and add: 0.160293996334076
And for 1000 keys/10,000 elements:
Your method: 14.6810429692268
Nil coalescing: 15.1537700295448
Optional chaining 14.4717089533806
Remove and add: 1.54668599367142
So here I have a basic setup
var preferenceSpecification = [String : String?]()
preferenceSpecification["Key"] = "Some Key"
preferenceSpecification["Some Key"] = nil
preferenceSpecification["DefaultValue"] = "Some DefaultValue"
print(preferenceSpecification)
var defaultsToRegister = [String : String]()
if let key = preferenceSpecification["Key"], let defaultValueKey = preferenceSpecification["DefaultValue"] {
defaultsToRegister[key] = preferenceSpecification[defaultValueKey]!
}
But the error points out where it demands that I force unwrap this, to be like this:
defaultsToRegister[key!] = preferenceSpecification[defaultValueKey!]!
Which doesn't make sense, because keyValue and defaultValue already are unwrapped
When you extract a value from a dictionary like this using subscript
[String: String?]
you need to manage 2 levels of optional. The first one because the subscript returns an optional. The second one because the value of you dictionary is an optional String.
So when you write
if let value = preferenceSpecification["someKey"] {
}
you get value defined as an optional String.
Here's the code to fix that
if let
optionalKey = preferenceSpecification["Key"],
key = optionalKey,
optionalDefaultValueKey = preferenceSpecification["DefaultValue"],
defaultValueKey = optionalDefaultValueKey,
value = preferenceSpecification[defaultValueKey] {
defaultsToRegister[key] = value
}
Suggestions
You should avoid force unwrapping as much as possible. Instead you managed to put 3 ! on a single line!
You should also try to use better name for your constants and variables.
You could also define an extension which helps get rid of the double optional situation.
extension Dictionary where Value == Optional<String> {
func flattened(_ key: Key) -> Value {
if let value = self[key] {
return value
}
return nil
}
}
Usage: preferenceSpecification.flattened("someKey")
A pattern I've gotten used to with Python's defaultdicts is a dictionary that returns a default value if the value for a given key has not been explicitly set. Trying to do this in Swift is a little verbose.
var dict = Dictionary<String, Array<Int>>()
let key = "foo"
var value: Array<Int>! = dict[key]
if value == nil {
value = Array<Int>()
dict[key] = value
}
I realize I can make a class that does this, but then the actual Dictionary has to be accessed through a property to use any of the other normal Dictionary methods
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
}
}
}
Is there a better pattern for this?
This changed in Swift 4, and there's now a way to read a key's value or provide a default value if the key isn't present. For example:
let person = ["name": "Taylor", "city": "Nashville"]
let name = person["name", default: "Anonymous"]
This is particularly useful when modifying dictionary values, because you can write code like this:
var favoriteTVShows = ["Red Dwarf", "Blackadder", "Fawlty Towers", "Red Dwarf"]
var favoriteCounts = [String: Int]()
for show in favoriteTVShows {
favoriteCounts[show, default: 0] += 1
}
I covered this change and others in my article What's new in Swift 4.
Using Swift 2 you can achieve something similar to python's version with an extension of 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 }
}
}
The closure after the ?? is used for classes since they don't propagate their value mutation (only "pointer mutation"; reference types).
The dictionaries have to be mutable (var) in order to use those subscripts:
// 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]
Unless I'm misunderstanding defaultdict in Python, I don't see how nil coalescing wouldn't work for you. Let's say you had a dictionary of type [Int:Int], and you wanted it to return 0 by default. With nil coalescing it looks like this:
let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4] ?? 0
You mentioned in a comment that that wouldn't work because it wouldn't update the dictionary. I don't understand the problem, though: why would you need to update the dictionary if you knew that every instance of nil would be replaced by your default? Maybe I'm missing something here but it seems like defaults and nil coalescing are (in practice) the same.
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 }
}
}
In this case, the example above could be written like this:
let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4, or: 0]
In this case, mutating methods can work on the keys, like this:
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]
This extension is similar to the default subscript in Swift 4, with the difference that it will actually store the default value in the dictionary.
(It's also similar to QByte's answer, with the difference that it uses an autoclosure to prevent accessing the default when not needed).
extension Dictionary {
subscript(key: Key, setDefault defaultValue: #autoclosure () -> Value) -> Value {
mutating get {
return self[key] ?? {
let value = defaultValue()
self[key] = value
return value
}()
}
}
}
Note that no setter is defined for the subscript as the standard default subscript already fulfills this purpose.
Example:
var items = [String: ComplexItem]()
let item1 = items["milk", setDefault: ComplexItem()]
let item2 = items["milk", setDefault: ComplexItem()]
Here the ComplexItem is only created once because the dictionary retained it after the first access.
I'm trying to loop through an (as I would interpret) array by arrayname.count like:
if var storedToDoItems: AnyObject? = NSUserDefaults.standardUserDefaults().objectForKey("toDoItems") {
toDoItems = []
for var i = 0; storedToDoItems.count; i++ {
toDoItems.append(storedToDoItems[i] as NSString)
}
}
and receive the error in the title marked by storedToDoItems.count. I'm populating first storedToDoItems like this:
...
toDoItems.append(tb_toDoItem.text)
let fixedToDoItems = toDoItems
// Store items
NSUserDefaults.standardUserDefaults().setObject(fixedToDoItems, forKey: "toDoItems")
// Save the stored stuff
NSUserDefaults.standardUserDefaults().synchronize()
...
If you store an array to the user defaults, you should try to cast it to an array when you extract it.
If toDoItems is defined as an array of strings [String], then you just have to use optional binding combined with optional cast, and just copy the extracted array to toDoItems:
if let storedToDoItems: [String] = NSUserDefaults.standardUserDefaults().objectForKey("toDoItems") as? [String] {
toDoItems = storedToDoItems
}
Being an array a struct, i.e. a value type, it is assigned by copy, so when assign storedToDoItems to toDoItems, a copy of it is created and assigned - so you don't need to manually add each element individually.
Your code instead has 2 errors:
you have defined storedToDoItems as an optional AnyObject, and optionals don't have a count property.
even if it is not defined as optional, AnyObject doesn't have a count property as well. It's irrelevant that the actual type stored in the variable is an array, the variable is declared as AnyObject and that is how is treated by the compiler.
You need to tell what's inside:
toDoItems = [String]()
if let storedToDoItems = NSUserDefaults.standardUserDefaults().objectForKey("toDoItems") as? [String] {
for item in storedToDoItems {
toDoItems.append(item)
}
}
and even shorter:
toDoItems = [String]()
if let storedToDoItems = NSUserDefaults.standardUserDefaults().objectForKey("toDoItems") as? [String] {
toDoItems = storedToDoItems
}
I just used this Bold line will work for you hopefully
if var toDoStored: [String] = NSUserDefaults.standardUserDefaults().objectForKey("toDoSaved") as? [String]{
tblItems = []
for var i = 0; i < toDoStored.count ; ++i {
tblItems.append(toDoStored[i] as NSString)
}
}
So I am trying to get the Actual Variable Name as String in Swift, but have not found a way to do so... or maybe I am looking at this problem and solution in a bad angle.
So this is basically what I want to do:
var appId: String? = nil
//This is true, since appId is actually the name of the var appId
if( appId.getVarName = "appId"){
appId = "CommandoFurball"
}
Unfortunately I have not been able to find in apple docs anything that is close to this but this:
varobj.self or reflect(var).summary
however, this gives information of what is inside the variable itself or the type of the variable in this case being String and I want the Actual name of the Variable.
This is officially supported in Swift 3 using #keyPath()
https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md
Example usage would look like:
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Wendy")
In Swift 4 we have something even better: \KeyPath notation
https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
NSPredicate(format: "%K == %#", \Person.mother.firstName, "Wendy")
// or
let keyPath = \Person.mother.firstName
NSPredicate(format: "%K == %#", keyPath, "Andrew")
The shorthand is a welcome addition, and being able to reference keypaths from a variable is extremely powerful
As per the updated from this answer, it is supported in Swift 3 via #keyPath
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Andrew")
This is my solution
class Test {
var name: String = "Ido"
var lastName: String = "Cohen"
}
let t = Test()
let mirror = Mirror(reflecting: t)
for child in mirror.children {
print(child.label ?? "")
}
print will be
name
lastName
This works:
struct s {
var x:Int = 1
var y:Int = 2
var z:Int = 3
}
var xyz = s()
let m = Mirror(reflecting: xyz)
print(m.description)
print(m.children.count)
for p in m.children {
print(p.label as Any)
}
I've come up with a swift solution, however unfortunately it doesn't work with Ints, Floats, and Doubles I believe.
func propertyNameFor(inout item : AnyObject) -> String{
let listMemAdd = unsafeAddressOf(item)
let propertyName = Mirror(reflecting: self).children.filter { (child: (label: String?, value: Any)) -> Bool in
if let value = child.value as? AnyObject {
return listMemAdd == unsafeAddressOf(value)
}
return false
}.flatMap {
return $0.label!
}.first ?? ""
return propertyName
}
var mutableObject : AnyObject = object
let propertyName = MyClass().propertyNameFor(&mutableObject)
It compares memory addresses for an object's properties and sees if any match.
The reason it doesn't work for Ints, Floats, and Doubles because they're not of type anyobject, although you can pass them as anyobject, when you do so they get converted to NSNumbers. therefore the memory address changes. they talk about it here.
For my app, it didn't hinder me at all because I only needed it for custom classes. So maybe someone will find this useful. If anyone can make this work with the other datatypes then that would be pretty cool.
Completing the accepted answer for extensions:
The property needs to be #objc.
var appId: String? {
....
}
You need to use #keyPath syntax, \ notation is not supported yet for extensions.
#keyPath(YourClass.appId)
The best solution is Here
From given link
import Foundation
extension NSObject {
//
// Retrieves an array of property names found on the current object
// using Objective-C runtime functions for introspection:
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
//
func propertyNames() -> Array<String> {
var results: Array<String> = [];
// retrieve the properties via the class_copyPropertyList function
var count: UInt32 = 0;
var myClass: AnyClass = self.classForCoder;
var properties = class_copyPropertyList(myClass, &count);
// iterate each objc_property_t struct
for var i: UInt32 = 0; i < count; i++ {
var property = properties[Int(i)];
// retrieve the property name by calling property_getName function
var cname = property_getName(property);
// covert the c string into a Swift string
var name = String.fromCString(cname);
results.append(name!);
}
// release objc_property_t structs
free(properties);
return results;
}
}