Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Enumerations and Optionals in Swift Introduction to Optionals Early Exits Using Guard

ABHINAV VERMA
ABHINAV VERMA
6,033 Points

If instead of checking if name and age properties are not nil using guard, we had to check address which is optional?

In the above question we checked for guard let name = friendDictionary["name"], let age = friendDictionary["age"] name and age should not be nil. If we had to check for address and specifically if the address contained a specific string, then how do we do optional comparison? For e.g if you had an array of structs of type Friend. You had to find the friend who had new york in his address and return the friend struct with that person's details, how would you proceed? I'm having difficulties comparing optional strings

1 Answer

Xavier D
PLUS
Xavier D
Courses Plus Student 5,840 Points

Hi,

I agree with having trouble with optionals, which is why I decided to revisit videos like this after completing the track (well...most of it...).

Anywho, I live in New York and trying to find a friend in this city is quite difficult....you never know who's shady here! Anywho again, what was also difficult was trying to follow the study without an actual dictionary to use to follow along.

Thus, based off of the keys that were unwrapped in the if lets and guard lets, I decided to mimic my own dictionary. So here...oh...since dictionaries primarily work with keys, I associated keys to the data of the name, age, and address dictionaries contained in the friendDictionary.

var friendDictionary = [
    "name": [
        "PP": "Pasan",
        "XD": "Xavier",
        "SD": "Seth"],
    "age": [
        "PP": "37",
        "XD": "36",
        "SD":"13"],
    "address": [
        "PP": "Treehouse Island",
        "XD": "Queens, New York"]
]

Now, I learned that if you want to search a dictionary based on a value like, "New York", you can use the values property along with the contains() method that are inherited by dictionaries. Now I tried to call them on the nested dictionary but it didn't show up in autocomplete. Thus, I figured that it may on work on a dictionary that is not nested. Therefore, I had to do this to unnest the address dictionary by doing...

if let address = friendDictionary["address"]
{
    address.values.contains("New York")
} 

However, I got a false in the results panel. Thus, I figured that may the values that I'm searching for has to be with precise spelling, so I modified the if let to look like...

if let address = friendDictionary["address"]
{
    address.values.contains("Queens, New York")
} 

Now that worked! So I guess we can search using values; however, I still feel comfortable with using keys.

Anywho, when modeling the new() function, I figured if was better to associated it as a method to the Person struct because it will be primarily be used along with the struct (I felt this made sense because the function would not work on other dictionaries that do not have keys named the same as the struct's three properties.

func new(friendInitials key: String, inFriendDictionary friendDictionary: [String:[String:String]]) -> Friend?
{
        var name = ""
        var age =  ""

        guard let nameDict = friendDictionary["name"], let ageDict = friendDictionary["age"] else
        {
            return nil
        }
        if nameDict.keys.contains(key) && ageDict.keys.contains(key)
        {
            for (initials, value) in nameDict
            {
                switch initials
                {
                case key: name = value
                default: break
                }
            }
            for (initials, value) in ageDict
            {
                switch initials
                {
                case key: age = value
                default: break
                }
            }

            if self.name == name && self.age == age
            {
                if let address = friendDictionary["address"]
                {
                    return Friend(name: name, age: age, address: address[key])
                }
                else
                {
                    return Friend(name: name, age: age, address: nil)
                }
            }
            else
            {
                return nil
            }

        }
        else
        {
            return nil
        }
}

Now I know Pasan used a [String: String] type for the friendDictionary parameter; however since you need to go through a dictionary to pull out other dictionaries, I felt it had to be nested, thus I put the type as [String:[String:String]] instead.

Also, I decided to add another a parameter that allows key input since dictionaries primarily rely on keys. Besides, having keys inputted to the dictionary is great, especially if you want to had more friends to the dictionary....so I added another method for that duty as well...

func new(friendInitials initials: String, addToFriendDictionary friendDictionary: inout [String:[String:String]] ) -> [String:[String:String]]?
{
        if self.address == nil
        {
            guard var name = friendDictionary["name"], var age = friendDictionary["age"] else
            {
                return nil
            }
            name.updateValue(self.name, forKey: initials)
            friendDictionary.updateValue(name, forKey: "name")
            age.updateValue(self.age, forKey: initials)
            friendDictionary.updateValue(age, forKey: "age")
            return friendDictionary
        }
        else
        {
            guard var name = friendDictionary["name"], var age = friendDictionary["age"] else
            {
                return nil
            }
            name.updateValue(self.name, forKey: initials)
            friendDictionary.updateValue(name, forKey: "name")
            age.updateValue(self.age, forKey: initials)
            friendDictionary.updateValue(age, forKey: "age")

            if var address = friendDictionary["address"]
            {
                address.updateValue(self.address!, forKey: initials)
                friendDictionary.updateValue(address, forKey: "address")
                return friendDictionary
            }
            else
            {
                return friendDictionary
            }
        }
}

Aside for those bit of changes, I tried to keep my model as close as the model that Pasan demonstrated. After completing the struct model. I decided to test it out, and since the methods are not static, I have to create an instance before calling the methods.

let someFriend = Friend(name: "Pasan", age: "23", address: "Treehouse Street")
let aFriendSearch = someFriend.new(friendInitials: "PP", inFriendDictionary: friendDictionary)

I was able to find Pasan in the dictionary! However, if I missed typed his initials, name, or age, the search will return nil. Since the address is optional, I didn't factor misspelling in the struct argument,

Lastly, I tested out adding to the dictionary with these three lines of code...

let yoMamaFriend = Friend(name: "Ramlal", age:"65", address: "San Fernando, Trinidad")
let friendDictionaryWithMa = yoMamaFriend.new(friendInitials: "MR", addToFriendDictionary: &friendDictionary)
print(friendDictionary)

Now my moms is in and with the following printed out....

["name": ["XD": "Xavier", "SD": "Seth", "MR": "Ramlal", "PP": "Pasan"], "age": ["XD": "36", "SD": "13", "MR": "65", "PP": "37"], "address": ["XD": "Queens, New York", "MR": "San Fernando Trinidad", "PP": "Treehouse Island"]]

Since address is optional, I set my moms address to nil since she doesn't live there anymore, which resulted in...

["name": ["XD": "Xavier", "SD": "Seth", "MR": "Ramlal", "PP": "Pasan"], "age": ["XD": "36", "SD": "13", "MR": "65", "PP": "37"], "address": ["XD": "Queens, New York", "PP": "Treehouse Island"]]

Hmm... it appears every is working fine. Thank you Treehouse for teaching me so much!