Swift Switch Statement

I'm rather new to programming and was wondering if there's a more concise way to accomplish the grammatical edits towards the end (ensuring that it doesn't return nonsensical statements like 1 bottleS) than what I've done here? I was thinking maybe a switch statement, but I am not familiar enough to implement one or even sure if that's the best approach. Thanks!

func beerSong(withThisManyBottles totalNumberOfBottles : Int) -> String { 

    var lyrics : String = ""    

    for i in (3...totalNumberOfBottles).reversed() {

        let newLine : String = "\n \(i) bottles of beer on the wall, \(i) bottles of beer. \n Take one down and pass it around, \(i - 1) bottles of beer on the wall.\n"

        lyrics += newLine

} 

lyrics += "\n 2 bottles of beer on the wall, 2 bottles of beer. \n Take one down and pass it around, 1 bottle of beer on the wall.\n"

lyrics += "\n 1 bottle of beer on the wall, 1 bottle of beer. \n Take one down and pass it around, no more bottles of beer on the wall.\n" 

lyrics += "\n No more bottles of beer on the wall, no more bottles of beer. \n Go to the store and buy some more, 99 bottles of beer on the wall.\n"

return lyrics

}

print(beerSong(withThisManyBottles : 99))

2 answers

  • answered 2018-07-11 03:48 rmaddy

    I would write a method to return the proper wording based on a number:

    func bottles(for count: Int) -> String {
        if count == 0 {
            return "no more bottles"
        } else if count == 1 {
            return "1 bottle"
        } else {
            return "\(count) bottles"
        }
    }
    

    Then I would refactor your beerSong method as:

    func beerBottleLine(for count: Int) -> String {
        let countBottles = bottles(for: count)
        let oneLess = bottles(for: count - 1)
        let line = "\(countBottles) of beer on the wall, \(countBottles) of beer.\nTake one down and pass it around, \(oneLess) of beer on the wall."
    
        return line
    }
    
    func beerSong(withThisManyBottles totalNumberOfBottles : Int) -> String {
        let lyrics = (1...totalNumberOfBottles).reversed().map { beerBottleLine(for: $0)}.joined(separator: "\n")
    
        return lyrics + "\nNo more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, \(totalNumberOfBottles) bottles of beer on the wall."
    }
    

    And a quick test:

    print(beerSong(withThisManyBottles: 5))
    

  • answered 2018-07-11 04:37 Alexander

    Here is my take on it. rmaddy's code computes beerBottleLine(for:) twice for most numbers (once when the number is the "main" number, and once again when it's the "one less" number). Admittedly, it's an absolutely tiny and essentially meaningless performance difference, but it demonstrates the use of the neat zip(a, a.dropLast()) pattern.

    I also opted to increase readability by taking advantage of multiline string literals, switching to more conventional identifier names, and by using a switch rather than 3 part if/else if/else ladder.

    func bottlePhrase(for count: Int) -> String {
        switch count {
            case 0: return "no more bottles of "
            case 1: return "1 bottle"
            case _: return "\(count) bottles"
        }
    }
    
    func beerSong(bottleCount: Int) -> String {
        let bottlePhrases = (0...bottleCount)
            .lazy
            .reversed()
            .map{ bottlePhrase(for: $0) + " of beer" }
    
        let mainBody = zip(bottlePhrases, bottlePhrases.dropFirst())
            .map { bottlePhrase, oneLessBottlePhrase in return """
                \(bottlePhrase) on the wall, \(bottlePhrase).
                    Take one down and pass it around, \(oneLessBottlePhrase) on the wall.
    
                """
            }
            .joined(separator: "\n")
    
        return mainBody + """
            \nNo more bottles of beer on the wall, no more bottles of beer.
                Go to the store and buy some more, \(bottleCount) bottles of beer on the wall.
            """
    }
    
    print(beerSong(bottleCount: 5))