Newbie questions on Swift-exercise

Hello people


I recently began my attempt at learning Swift using the Apple tutorial book and playgrounds. I have no experience with Swift or in programming, so it’s been a steep hill, but still, it’s been interesting.


However, I’ve now reached a point where I can’t seem to find the answers for an exercise in the Apple playground; the exercise – Counting Votes.


First issue: The calculation in my loop seems to be off.


the loop in question:


var yesVotes = [0]

var noVotes = [0]


for vote in shouldMascotChangeVotes {

if vote == true {

yesVotes += [1]

} else {

vote == false

noVotes += [1]

}

}

yesVotes.count

noVotes.count


The array supplied by Apple to the exercise only has 200 votes, yet my loop calculation returns a total of 202 votes. I can't see where the extra votes comes from?


Second issue:

according to the exercise, the next step is to write a function that takes two arguments, forIssue as a String and withVotes as Bool. The result of printResults should be “Should we change mascot? 54 yes, 23 no”.


So, my attempt at the function so far is thus:


func results(forIssue: String, withVotes: Bool) -> String {


for vote in withVotes {

if vote == true {

yesVotes += [1]

} else {

vote == false

noVotes += [1]

}

}


}


I'm pretty certain the function is wrong in a bunch of places and still not finished, but my question is primarily that I don't understand the error-message for the for vote in withVotes line. The message reads "Type 'Bool' does not conform to protocol 'Sequence'... Any explanation on this would be greatly appreciated!


Also, thank you very much for all the explanations in many other questions that have helped me in my attempts so far!


Cheers



(also, if this question is in the wrong forum, sorry about that)

Replies

On the first problem, you're doing something unnecessarily, which results in accidentally doing it wrong. When you declare yesVotes like this:


var yesVotes = [0]


you're declaring an array of Int values, and you set the first one to the value 0. Ditto noVotes. Then you append 200 votes either to yesVotes or noVotes, for a total of 200 + 1 + 1 = 202, because you started with an "extra" value in each array. You could fix this by starting with empty arrays, declared in one of these ways (among others):


var yesVotes: [Int] = [] // or ...
var yesVotes = [] as [Int]


You have to say what it's an array of, because [ ] is an empty array, but the compiler doesn't know what kind of values you intend.


However, the unnecessary part is that you don't need to use an array, since you just need the vote count, so you can do this:


var yesVotes = 0
var noVotes = 0

for vote in shouldMascotChangeVotes {
    if vote { // == true is unnecessary, this means the same thing
        yesVotes += 1
    } else {
        //vote == false ... you don't need this, it doesn't do anything
        noVotes +=1
        }
}
print (yesVotes)
print (noVotes)


On the second problem, the second parameter is a true/false value, not an array. The "for…in" construct is for iterating through an array (or some other kind of Sequence, hence that error message). I'm not sure exactly what's intended, but the fact that "withVotes" is plural suggests you should declare the function like this:


func results(forIssue: String, withVotes: [Bool]) -> String // 2nd parameter is array of Bool


and you would, of course, need the same counting fix as in the first problem, inside the body of the function.

Thank you! The loop finally returns a correct number of votes 🙂


however, the function started to just run and run. At least 500000 times before I stopped it. Which seems massive and slowing the Playground down to almost useless. And then, suddenly it stopped doing that again. Now nothing happens. Extremes, I guess.


my function;


func Results(forIssue: String, withVotes: [Bool]) -> String {



var yesVotes = 0

var noVotes = 0


for vote in withVotes {

if vote {

yesVotes += 1

} else {

noVotes += 1

}

}


print(yesVotes)

print(noVotes)


return ""

}

I've tried changing the withVotes with shouldMascotChangeVotes and this still doesn't move the function in any way. Also, yes, fully aware that the return line is wrong but I'll look into that when I understand the behaviour above it.

Please show all the code so we see how you create the array of votes that you pass to the function and the context in which the function is called.

(By the way, the conventional way to name a function in Swift is to make the first letter lower case, as you did in your first post.)

The array is one already created by Apple for the exercise, which is why I never thought about including it here. That would have been a good idea, I see now.


However, last night brought a solution, not least due to the suggestions in this thread.


If anyone is interested, the function ended up like this: (with a first letter lower case 🙂)


func results(forIssue: String, withVotes: [Bool]) -> String {

var yesVotes = 0

var noVotes = 0

for vote in withVotes {

if vote {

yesVotes += 1

} else {

noVotes += 1

}

}

print(yesVotes)

print(noVotes)

return "\(forIssue)" + " " + "\(yesVotes)" + " " + "yes" + " " + "\(noVotes)" + " " + "no"

}

results(forIssue: "should we change mascot?", withVotes: shouldMascotChangeVotes)

print(results(forIssue: "should we change mascot?", withVotes: shouldMascotChangeVotes))

Was pretty confused about the forIssue string and the withVotes declarations. Your reply put me on track. Somehow had the issue of votes not clearing after each poll so..


func tallyResults(forIssue:String, withVotes: [Bool]) {

let totalVotes = withVotes.count

for voteIssue in withVotes {

if voteIssue==true {

yesVotes += 1

} else {

noVotes += 1

}

}

if yesVotes > noVotes {

print("\(forIssue) passed with \(yesVotes) votes for and \(noVotes) against.\n Yes: \(yesVotes). No: \(noVotes). Total Votes: \(totalVotes)")

} else {

print("\(forIssue) did not pass with \(noVotes) votes against and \(yesVotes) for.\n Yes: \(yesVotes). No: \(noVotes). Total Votes: \(totalVotes)")

}

// Clear vote results to prevent double counting

yesVotes = 0

noVotes = 0

}

tallyResults(forIssue: "Should we change the mascot", withVotes: shouldMascotChangeVotes)

tallyResults(forIssue: "Should we install coffee vending machines", withVotes: shouldInstallCoffeeVendingMachineVotes)

tallyResults(forIssue: "Should we have more poll options vote", withVotes: shouldHaveMorePollOptionsVotes)

Thanks that was useful, helped me with a couple of mistakes.

I think you've still got some unnecessary steps though. You don't need to count because for ... in will automatically stop at end of array. Also you don't need to escape or put in quotes forIssue when printing because it's already a String.

My version:



var yesVotes = 0
var noVotes = 0

func printResults(forIssue issue: String, withVotes votes: [Bool]) {
    for vote in votes {
        if vote {
            yesVotes += 1
        } else {
            noVotes += 1
        }
    }
        print(issue + " \(yesVotes)"  + " yes, " + " \(noVotes)" + " no ")
    yesVotes = 0
    noVotes = 0
}

printResults(forIssue: "Should we change the mascot?", withVotes:shouldMascotChangeVotes)
printResults(forIssue: "Should we install coffee vending machines?", withVotes:shouldInstallCoffeeVendingMachineVotes)
printResults(forIssue: "Should we have more poll options?", withVotes:shouldHaveMorePollOptionsVotes)

// prints: Should we change the mascot? 125 yes,  75 no 
// prints: Should we install coffee vending machines? 100 yes,  100 no 
// prints: Should we have more poll options? 74 yes,  126 no

Hi. This may seem like a really stupid question but I am a complete newbie to Swift and programming in general.


How does the yesVotes know to count the trues and visa versa?

Votes is an array of Bool, either true or false.


the loop:


    for vote in votes {
        if vote {
            yesVotes += 1
        } else {
            noVotes += 1
        }
    }


explores all the votes in the array.

If the vote is true (yes), then add 1 to yesVotes counter, otherwise adds 1 to noVotes.


You have dedicated Swift functions to do it directly : reduce.


It works like this:

let votes = [true, false, false, true, true, true]
let yesVotes = votes.reduce(0, { $0 + ($1 ? 1 : 0) })
print(yesVotes)


Which is a bit cryptic at first but convenient.


Look here for other solutions :

h ttps://stackoverflow.com/questions/39984933/how-can-i-find-a-number-of-true-statements-in-an-array-of-bools-in-swift

You have dedicated Swift functions to do it directly :

reduce
.
reduce
is a super useful technique but here it just complicates things. In this case it’d be better to use
filter
. For example:
let votes = [true, false, false, true, true, true]
let yesVotes = votes.filter{ $0 }.count
print(yesVotes)

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

You're totally right, reduce is just too complex. The SO also proposed filter, much easier.

Here is the way I did it:


var yes = 0

var no = 0

for vote in shouldMascotChangeVotes {

if vote {

yes += 1

} else {

no += 1

}

}

print(yes)

print(no)

if yes > no{

print("Your bill passed!")

} else if yes < no {

print("This one failed!")

} else {

print("It's a tie! We need one more vote!")

}

SUre you can do this simple way.

But filter to count yes and no is a better way.