SpareTime
SpareTime

Reputation: 341

Swift Extract Numeric Value from Dictionary

I need to extract data from a dictionary (attributes from NSXMLParser, but I don't think that matters). The below works but is this really the "easiest" why to do it? The attribute may or may not exist in the dictionary. The value of the attribute may or may not convert to an integer (i.e. toInt() returns an optional). 'mandatory' is a Bool and 'minimumLength' is an Int and are class properties.

  func decodeDataRestrictions(#attributeDictionary: [NSObject: AnyObject]!) {
var stringValue: String?
var intValue: Int?

// Extract data restrictions from the element attributes
self.mandatory = false
stringValue = attributeDictionary["mandatory"] as String?
if stringValue != nil {
  if stringValue! == "true" {
    self.mandatory = true
  }
}
self.minimumLength = 1
stringValue = attributeDictionary["minimumLength"] as String?
if stringValue != nil {
  intValue = stringValue!.toInt()
  if intValue != nil {
    self.minimumLength = intValue!
  }
}

in Objective-C it was much easier:

    self.mandatory = NO;
if ([[attributeDict objectForKey:@"mandatory"] isEqualToString:@"true"]) {
  self.mandatory = YES;
}
self.minimumLength = 1;
if ([attributeDict objectForKey:@"minimumLength"] != nil) {
  self.minimumLength = [NSNumber numberWithInteger:[[attributeDict objectForKey:@"minimumLength"] integerValue]];      
}

Upvotes: 2

Views: 1142

Answers (2)

Airspeed Velocity
Airspeed Velocity

Reputation: 40963

You ought to be able to write that whole function as follows:

func decodeDataRestrictions(#attributeDictionary: [NSObject: AnyObject]!) {

    if (attributeDictionary["mandatory"] as? String) == "true" {
        self.mandatory == true
    }

    if let minimumLength = (attributeDictionary["minimumLength"] as? String)?.toInt() {
            self.minimumLength = minimumLength
    }
}

If you need to check an optional for nil-ness, and then use the value if it isn’t nil, then if let combines these two things, setting a local variable to the unwrapped value if non-nil. This is what’s happening with minimumLength, along with some optional chaining (i.e. if the value is non-nil, move on to do toInt() else nil).

In the case of mandatory, you can compare an optional value to a non-optional value with ==, so there’s no need to check for nil at all.

edit: having read your Objective-C version, you can simplify it even further if you’re happy to default the self values even in cases of missing dictionary data, as you’re doing there:

func decodeDataRestrictions(#attributeDictionary: [NSObject: AnyObject]!) {

    self.mandatory = (attributeDictionary["mandatory"] as? String) == "true"        
    self.minimumLength = (attributeDictionary["minimumLength"] as? String)?.toInt() ?? 1

}

The minimumLength version uses the nil-coalescing operator, which substitutes a default value from the right-hand side in case of nil on the left-hand side.

Upvotes: 2

Antonio
Antonio

Reputation: 72760

You should use optional binding - the first extraction can be simply written as:

if let mandatory = attributeDictionary["mandatory"] as? String {
    self.mandatory = mandatory == "true"
}

whereas the 2nd requires an additional check:

if let minLen = attributeDictionary["minimumLength"] as String? {
    if let minLen = minLen.toInt() {
        self.minimumLength = minLen
    }
}

the first if verifies that a value for the mandatory key exists, the second verifies that the value is convertible to an integer.

Upvotes: 1

Related Questions