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 triallance samaria
3,642 PointsUsing a for-in loop with a switch statement
Earlier in previous examples Amit used for-in loops with a switch statement to iterate through an array. I tried using that with the Enum code and I keep getting errors. I know there are diff ways to do things so i want to try it an alternate way. I keep getting swift errors, here's my code:
let daysOfWeek = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
func weekdayOrWeekend(day: String) -> String? {
for day in daysOfWeek{
switch day{
case 0...4:
return "It's a weekday"
case 5..<day.count:
return "It's a Weekend"
//I also tried
// case 5,6:
// return "It's a Weekend"
default:
return "Not a valid day"
}
}
return nil
}
if let randomDay = weekdayOrWeekend("Monday"){
println(randomDay)
}
Steve Hunter
57,712 PointsHans,
You've missed an if
at the beginning. You just start by testing whether month == 1
but that's not preceded by the if
keyword. I think the test being tested needs to be surrounded with brackets too:
if (month == 1) {
// do stuff
} else if (month == 2) {
// do some other stuff
} ... etc ...
That should help. If not, please shout!
Steve.
16 Answers
Steve Hunter
57,712 PointsHi there,
Your cases aren't going to work like you envisage, I don't think. You're trying to use their index position within the array but the for-in loop will use the value in the array.
In the first iteration of for day in daysOfWeek
the variable day
will hold "Monday", not zero. However, you've also passed in day
as a parameter for your method. I think the day
in the for-in loop will be out of scope of the incoming parameter. But either way, there will be no number associated with either, so the switch won't functon as you hope.
You need to combine the two methods here, first, we need to loop through your array to see if there's a match. Then, process the point in the array, its index, with your switch/case approach.
func dayType (day: String) -> String {
var typeOfDay: String = ""
for var i = 0; i < daysOfWeek.count; i++ {
if(day == daysOfWeek[i]){
// process the array index
switch i {
case 0...4: typeOfDay = "It's a weekday"
case 5, 6: typeOfDay = "It's weekend!"
default: typeOfDay = "Unlikely to be a day"
} // end switch
} else {
typeOfDay = "Not a day"
} // end if
}// end for
return typeOfDay
} // end func
Here, I've used a standard for
loop. This uses an index counter i
that is incremented at each iteration. At each loop, we test to see if the value passed in as a parameter, day
is equal to the contents of the array at daysOfWeek[i]
. If it isn't, we carry on with the loop. If the two are equal, we pass the index i
into the switch
statement that you created.
That switch statement sets a string which is returned, as required.
I hope that makes sense!
Steve.
Hans Lohmann
414 Points@ Steve Hunter Thanks for looking at that code. Someone pointed that out to me a a little earlier. I haven't had a chance to enter it yet. I switched over to Objective C, and I started working at midnight now its 7 am I need to get some rest soon. Thanks
Steve Hunter
57,712 PointsGet some sleep! :-)
lance samaria
3,642 Points@Steve, thanks for the answer. I see where I made the mistake of thinking the for-in loop would iterate through by index instead of value. One quick question, why did you need the 'else'? The 'if' combined with the 'switch' contains all the possible outcomes.
@Hans, thanks for looking at my question :)
Steve Hunter
57,712 PointsThe if
and switch
don't capture all the possible outcomes - that's why the else
is there.
Work it through - you're dealing with a user-input function here - the user uses your function in their app a bit wrong and enters dayType("tomorrow");
. What happens?
The switch case is never reached; the string day
is never equal to a member of the array at any index. So, the function would skip the if
and reach the return
, returning a wholly unhelpful empty string as a legacy of its initialisation, rather than any text. So, I use an else
to capture the scenario that the user may enter a mistyped day, or a nonsense string. In the alternative, you could initialise the string to something like "Not a day" but that isn't quite as clear. My preference, I guess.
It is, as you say, about handling all eventualities. When your code accepts parameters it is dangerous to assume that the right parameters will be passed in so having code that is verbose in its output helps pin down the route the code is taking. Outputting blank strings or nulls can be hugely confusing! Here, if you get a "Not a day" output, you know why. Swift insists on the default
statement although, in this example, that is impossible to reach. The else
is very easy to get to, though.
I hope that makes sense.
Steve.
lance samaria
3,642 Pointsahhhhhh, I see your point with misspellings and foreseeing all possible outcomes, but wouldn't the default be triggered "Unlikely to be a day"? It seems the default would take care of that.
Steve Hunter
57,712 PointsNo - the switch case is only reached if the parameter entered equals one of the elements of the array.
The default is never reached in the current guise - all the case
statements cover all positive if
outcomes. The default:
would only be reached if I forgot to implement all the outcomes 0 ... 6.
Make sense?
Steve Hunter
57,712 PointsWalk the code through with a misspelled parameter ... you'll see what I mean (and you'll end up in the else
clause!).
lance samaria
3,642 PointsI ran it, your right, no matter what I type in I can't trigger the default statement. Even after I remove the else. As you said earlier the Swift language requires it. Just seems odd I can't get to it in this scenario.
lance samaria
3,642 PointsThank you very much for your help and very fast answers! :-)
Steve Hunter
57,712 PointsIt isn't odd, no.
The default
is triggered when none of the case
tests match what is passed to the switch
in the current iteration. In our example, we have a totally closed and controlled switch
- we gain control with the day == daysOfWeek[i]
as we only progress into the switch
when we have a direct match, a true
boolean outcome. We have validated the parameter passed in.
The array is fixed with 7 elements - that is immutable. The switch
has all seven outcomes handled - each array element, 0 to 6, have a case
representing them. So, it is impossible for the default
to be triggered.
If we approached this from where you started this and switch
on the value passed into the function and try matching that to the array elements, then the default
would handle the erroneous entries as we don't validate the entry outside of the switch
. It would be ugly code but:
func daySwitch(day: String) -> String {
switch day {
case "Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday": return "Weekday"
case "Saturday",
"Sunday": return "Weekend"
default: return "Not a day"
}
}
This negates the need to declare your array completely but covers all eventualities and uses default
to pick up the pieces of any mistakes. It remains, however, inelegant code.
Hope that all makes sense!
Steve.
lance samaria
3,642 PointsI understand now. In the example when you used the for-loop, the switch is contained inside the for-loop's block of code, since it iterates through the array using the index # (daysOfWeek[i]), if the user enters a parameter that doesn't match a value in the array, the for-loop kicks over to the else because it doesn't see the default (because it has no index # association it's invisible to the for-loop). Basically the for-loop is in control of the switch and the default doesn't have a index #. I get that.
In the example you just wrote the the switch is in control and if the user doesn't enter a matching value the default does trigger because it's like like the for-loops else.
correct?
Steve Hunter
57,712 PointsPerfecto - spot on, yes. Full marks. :-)
Steve.
lance samaria
3,642 PointsThank you very much!!! :-)
Steve Hunter
57,712 PointsNo problem! Glad to have helped!
Steve.
lance samaria
3,642 PointsSteve, one more question. Answer whenever you can. I redid the code using an integer for the argument and this time using the for-in loop (I understand it goes through by value), but when i create a variable to hold my function, no matter what # I put inside the argument, I get the same exact string "It's a weekday":
let daysOfWeek = [1, 2, 3, 4, 5, 6, 7]
func weekdayOrWeekend(day: Int) -> String? {
for day in daysOfWeek{
switch day{
case 1...5:
return "It's a weekday"
case 6,7:
return "It's a weekend"
default:
return "Not a valid day"
}
}
return nil
}
//variable with function
var randomDay = weekdayOrWeekend(15)
Steve Hunter
57,712 PointsIf we go back to the start on this - I mentioned a scope problem.
The parameter you pass in, day
can be referenced inside the function without any problem. However, If you create a new variable, also called day
, that will be a completely different bit of memory. So, the day
you've passed in as a parameter has nothing to do with the index of the for
loop that also happens to be day
.
The for
loop uses a variable to hold the current iteration value as it loops through. You've called that day
. That variable exists only inside the for
loop and has no link to the day
parameter you've passed into the method.
This bit of code loops through the array. As soon as its first iteration starts, day
is 1 (irrespective of what you passed into the function for the reasons above). That satisfies the first case
and it returns out of the function - no further execution occurs. Your parameter is never used.
for day in daysOfWeek{
switch day{
case 1...5:
return "It's a weekday"
case 6,7:
return "It's a weekend"
default:
return "Not a valid day"
}
}
Think about what you're trying to achieve here. You're looping through an array. At each loop, you want to do something. Does that involve the parameter day
? Probably. This example here makes the array serve no purpose. looping through the array adds nothing. The switch
case applies functionality to each of those numbers, but they don't need to link to the array at all. Just the same as my last switch
case did away with the array; this is exactly the same but with integers rather than strings.
I think the best thing to do is to do what a Business Analyst would do - define the requirements; what is this function supposed to do? Put that into words then worry about writing code. What does the array do? How is the parameter day
used & why? How is the function going to be used?
As you've presented it, you want a function that takes an integer as a parameter and returns one of three strings depending on its value. Why does the array exist? (Bounds checking?)
The solution to that problem is a modification of my last string-based switch
code:
func daySwitch(day: Int) -> String {
switch day {
case 1...5: return "Weekday"
case 6, 7: return "Weekend"
default: return "Not a day"
}
}
No looping; no array but job done? What's the loop achieving that the above code doesn't? How does the array bring anything to the party?
Maybe the array isn't consecutive numbers - maybe it's your bingo card. As each number is drawn, you want to know if you've got that number, or not. Now we have a need for a loop and an array - that would be a real use for that type of function.
Let's have a quick go ...
let myCard = [10, 3, 5, 34, 22, 17]
func numberDrawn(ball: Int) -> String{
for number in myCard { // number will be each value above, in succession
if(number == ball){ // does the current value from the array equal the parameter?
return "Tick that box" // Yes it does.
}
}
return "Next ball please!" // no it doesn't
}
Not a great example, after all - we don't need a switch
.
I'll try to think of another one ... I'll post this for now.
I'm trying to illustrate when best to use each type of construct. Your use of the loop isn't quite right due to scoping but also due to what a loop is there to do. That context is contrary to what you're trying to use it for. You were using, you thought, the parameter you passed in at the beginning of the for
loop - presumably hoping for a test of equality? That's not quite right.
I hope that's of some use to you.
Steve.
lance samaria
3,642 PointsI have to look your notes over more intently. See I understand loops and conditionals, but using them in the right context seems to be my problem. When to use a 'for' vs a 'for-in' and an 'if-else' vs a 'switch' and combining then them -or not- is where it gets tricky. The first question you answered for me I understand 100%. For this example I do see your point in saying the for-in loop is pointless as the switch statement accomplishes the same exact task. I see why your saying the array is pointless for days of the week but if it were something such as school test scores I would think you would need to give the user something to reference from eg. let grades = [0...60, 61...69, 70...80, 81...90, 91...100] and using a switch would easily give grade scores F-A as instead of looping through the grades.
It makes sense to write the problem out first and then try to find the easiest way to solve it. I was at the beginning of the Enum track and wanted to try to accomplish this without using Enums.
I'll try to get it down packed and send you a message later. Thanks again!
Steve Hunter
57,712 PointsThe grades example you came up with is a great example of where a switch/case
construct can be used. I'd implement it slightly differently.
So, you could have an array of all the test results in the array - so the marks for each individual student; loop through that array and count how many students are in each grade bracket using the switch/case. Inside each case
, increment a counter for each grade.
There are much better ways of doing that but the task does require a loop and a switch/case! And you've got no link to the student's names, but hey.
You say you "wanted to try to accomplish this without using Enums" - it's the this that needs defining. What's the problem we're trying to solve.
Steve.
lance samaria
3,642 PointsThis is something I came up with in the shower just now. I'm not sure if it's exactly what you were describing but it let's you input a student's name and associates that name with whatever score and grade you enter.
func studentNameAndGrade(#studentName: String, #studentGrade: Int) -> String{
var student = studentName
var score = studentGrade
switch studentGrade {
case 0...59:
return "\(student)'s score is: \(score) and grade is a F"
case 60...70:
return "\(student)'s score is: \(score) and grade is a D"
case 71...80:
return "\(student)'s score is: \(score) and grade is a C"
case 81...90:
return "\(student)'s score is: \(score) and grade is a B"
case 91...100:
return "\(student)'s score is: \(score) and grade is an A"
default:
return "Invalid score please re-enter a score between 0-100"
}
}
var sample = studentNameAndGrade(studentName: "Rael", studentGrade: 99)
sample
Steve Hunter
57,712 PointsNice, yes. That does the job. One thing - remember D.R.Y? "Don't Repeat Yourself" is a good coding rule.
So to tidy that up a bit we could do something like:
func studentNameAndGrade(#studentName: String, #studentGrade: Int) -> String{
var student = studentName
var score = studentGrade
var grade: String
switch studentGrade {
case 0...59:
grade = "F"
case 60...70:
grade = "D"
case 71...80:
grade = "C"
case 81...90:
grade = "B"
case 91...100:
grade = "A"
default:
return "Invalid score please re-enter a score between 0-100"
}
return "\(student)'s score is: \(score) and grade is a \(grade)"
}
Just looks a little neater, perhaps.
lance samaria
3,642 PointsYour absolutely right. I was thinking about it earlier. Thank very much for your help. I'm sure I'm going to have many more questions.
Talk to you soon!
āā
Steve Hunter
57,712 PointsNo problem - ask away!
Talk soon, yes.
Steve.
Hans Lohmann
414 PointsHans Lohmann
414 PointsIt looks like you are only a little ahead of me, I am having some some trouble with for-in loop statement and if statement, I'll give you a screen shot of what I have if you can help me I would appreciate it. I can check back her later or you can write to me. If I get to where you are before you solve the answer I'll help you too.