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

Tuples: Calling data from dictionaries

func searchMusic (#info: String) -> [title: String, artist: String, album: String] {

    let button = ["1","2","3"]
    let title =
        ["BD" : "Black Dog",
         "ST" : "Shoot to Thrill",
         "SN" : "Still of the Night"]
    let artist =
        ["LZ" : "Led Zeppelin",
         "AC" : "ACDC",
         "WS" : "White Snake"]
    let album =
        [ "Z" : "Zoso",
          "B" : "Back in Black",
          "S" : "Self-Titled"]

    var selection = [title, artist, album]

    for choice in button {
        if "1" == info {
            selection = [title["BD"], artist["LZ"], album["Z"]]

        }
    }
        return selection

}


searchMusic("1")

I am trying to do the extra credit problem in the "parameters and tuples" section in the iOS track. I essentially need to create three keys you see above (title, artist, album), and be able to call them in a tuple. I have it set up kind of like a juke box where if you press button "1", it will call the first set of information in each dictionary for they are related. The song "Black Dog" is sung by "Led Zeppelin", on the "Zoso" album, and respectively the same for the others.

When calling the "searchMusic" function and passing it a "1", nothing is happening. Anyone have some advice? Thanks.

1 Answer

Hi Jason,

First off good attempt, you have a good mindset for the type of logic required however you appear to have become confused when it comes down to what to use where, just to give you a heads up below is some code that I wrote quickly that solves the problems mentioned below but bare in mind that the code itself might be confusing so please read through my comments to ensure it makes sense.

Back to your original code, it has the following problems:

  1. [title: String, artist: String, album: String] isn't valid, tuples are always declared using parentheses

  2. your info parameter is named therefore your function called needs to be declared as searchMusic(info: "1") instead, if you didn't intend to make this named you can simply remove the hash/pound before info: String

  3. your info parameter can also be an integer instead which makes more sense since button are of a base type number when you think about, just food for thought

  4. your for loop is redundant in this situation as you're not iterating over any relatable data, the IF statement by itself would suffice

  5. your data is disconnected, what I mean by that is your dictionaries share a common pattern but aren't connected making data calls ambiguous since you could end up with an incorrect song for an artist, it's better to store relatable data in the same place to avoid issues down the track.

Now to the fun part

As I said I've written the below example which solves all of the above and more including extensibility which is vital for an app to be able to grow over time.

let musicCollection: [Dictionary<String, String>] = [
    [
        "album": "Zoso",
        "artist": "Led Zeppelin",
        "title": "Black Dog"
    ],
    [
        "album": "Back in Black",
        "artist": "ACDC",
        "title": "Shoot to Thrill"
    ],
    [
        "album": "Self-Titled",
        "artist": "White Snake",
        "title": "Still of the Night"
    ]
]

func searchMusic(button: Int) -> (title: String, artist: String, album: String)? {
    if button > 0 && button <= musicCollection.count {
        let song = musicCollection[button - 1]
        return (song["title"]!, song["artist"]!, song["album"]!)
    }

    return nil
}

let button: Int = 1
if let song = searchMusic(button) {
    println(song.title)
} else {
    println("No song data exists for \(button)")
}

Again, please don't freak out, I'll explain what this does different.

musicCollection array

The biggest difference and it's one of my above points in the data is grouped together which eliminates the possibility of ever getting incorrect data, another advantage to using an array is you can target the values using an Int which makes your code much more robust and less likely to break as I'll explain later on within the function itself.

Another thing you will notice aside from the fact there's nested dictionaries within the Array is that I've declared Dictionary<String, String>, as of Xcode 6.1 types are slightly stricter because optionals have been extended to more areas of the Swift language therefore we need to declare an explicit type for our array data to ensure our code within the function works correctly, of course you can also swap it out for NSDictionary but this would require you to typecast the values in the function as a String which is ugly compared to unwrapping the object itself.

Don't worry, that might sound confusing but trust me it's a very simple concept that get's lost in translation far too often, sadly Apple doesn't explain this so it's up to the little guy to work out key differences.

searchMusic function

Now onto the fun code, fundamentally I'm doing the same thing you were trying to achieve in your code, the biggest difference is I'm relying on the input from button and the musicCollection array to get my data, what this allows me to do is always ensure button is a valid Int greater than zero but no larger than the total length of musicCollection.

Before I go on another thing you will notice is I've added a question mark to the end of the tuple, as you will remember this notifies the function that an optional value can be present in the return type which in Swift is nil, this offers much greater control when calling the function if the item in the array doesn't exist allowing for a nice error rather than an application crash.

Back on track you will notice in the IF statement I've written button <= musicCollection.count, this is the magic of the code and is what prevents our code from trying to reach an array index greater than the total length hence giving us a VERY consistent result and no unexpected errors.

Inside the IF statement all I'm doing is grabbing the Dictionary data for the given index offset, in this example you have said you wanted a jukebox which typically starts at one which doesn't work since array's always start at zero, to overcome this problem we can simply minus 1 from button which will give us a correct index to work with, next I return the song data by unwrapping each key, value pair otherwise the value returned will be of the type [String: String] rather than String, finally I return nil so we can correctly check for an optional value.

Making the call

YAY, after all that reading we've made it to the execution part, probably the easiest code to understand the most simple, all I'm doing here is assigning a constant the value of 1 and passing that to the searchMusic call within an IF LET statement; if all has gone according to plan we should see Black Dog as the song title.

Changing this value to either 0 or 4 will result in the no song exists message.

Wrapping up

Hopefully that wasn't too harsh on your brain, please don't hesitate to ask as many questions as you like as I'm sure you will have many.

Happy coding!

This answer should be a sticky. I Had asked my own question regarding this extra credit section and I must say that your answer, Chris Upjohn has been very elegant and insightful.

I am still having a hard time understanding some of the concepts such as the clever use of the number (I think we owe Jason Martinez for that one). But if not for that, I have the following questions:

1) What would've been another way to call the dictionary?

2) In the lesson's video, Amit Bijlani includes the constant (in his case the array alone) within the function. Why can't we do that here?

Thanks in advance for your answers!