2

I'm starting with Swift and I'm stuck with this problem for a while now. I'm trying to go through an array of cards and add them into a dictionary under a key that represents the turn they were played on.

I made a dictionary turnsWithCardsPlayed that should contain a key:value pairs like this - "Turn 2":[Card1, Card2, Card3]. The problem is if the key has no value associated with it yet, it doesn't append the card.

let turnsWithCardsPlayed = [String: [Card]]()
for card in arrayOfCards {
    turnsWithCardsPlayed["Turn " + card.turn]!.append(card)
}

I solved the problem by including an if statement that checks if there is any value and if it is not, it creates a blank array and then appends the card. However the solution is clunky and too long in my opinion. Is there a better way to do it?

let turnsWithCardsPlayed = [String: [Card]]()
for card in arrayOfCards {
   if var turnCardArray = turnsWithCardsPlayed["Turn " + card.turn] {
      turnsWithCardsPlayed["Turn " + card.turn]!.append(card)
   } else {
      turnsWithCardsPlayed["Turn " + card.turn] = []
      turnsWithCardsPlayed["Turn " + card.turn]!.append(card)
   }
}

Thank you all :)

4 Answers 4

2

You could simplify it by using a ternary operator:

for card in arrayOfCards {
    turnsWithCardsPlayed["Turn " + card.turn] == nil ? turnsWithCardsPlayed["Turn " + card.turn] = [card] : turnsWithCardsPlayed["Turn " + card.turn]!.append(card)
}
Sign up to request clarification or add additional context in comments.

4 Comments

This wouldn't append the card when turnsWithCardsPlayed["Turn " + card.turn] == nil.
Yes it does, if turnsWithCardsPlayed["Turn " + card.turn] == nil then I assign an array containing the card: turnsWithCardsPlayed["Turn " + card.turn] = [card], and if not nil then I append the new card with turnsWithCardsPlayed["Turn " + card.turn]!.append(card). BTW I've tested it in a Playground.
Ah, sorry - I withdraw my comment!
Thanks, great and simple way, exactly what I meant to do initially. I'm kind of embarrassed I didn't think of it myself though :/ Marked as the answer
1

I know you have found your own solution, however please let me to describe a "swifter" and more functional programming approach. (IMHO of course).

1. Your solution does not compile

Yeah, probably you have already corrected it. But in your code you are declaring turnsWithCardsPlayed as constant

let turnsWithCardsPlayed = [String: [Card]]()

and then you are changing it. This is not allowed.

2. Now my solution: Card

Let's assume Card is a struct (maybe you have a class) declared as follow:

struct Card {
    let turn: Int
}

Fine, lets add a computed property to the struct (it will be useful very soon).

struct Card {
    let turn: Int
    var key: String { return "Turn \(turn)" }
}

3. The cards array

This is just a style preference, however if you have an array of Card I feel the natural name of the variable should be simply cards. There's no point in repeating into the name of a variable the type of the variable itself.

So

let cards = [Card(turn: 0), Card(turn: 1), Card(turn: 1), Card(turn: 2), Card(turn: 1), Card(turn: 0)]

4. Avoiding the bad guy "!"

One thing we need to remove from your code is this guy ! because he has the power to crash your entire app. Every good Swift programmer should be really afraid of him.

5. Functional programming

Now, you want simply reorganize the elements in cards and append every card to the correct slot of a dictionary. This can do with the reduce method.

So

let turns = cards.reduce([String:[Card]]()) { (var accumulator, card) -> [String:[Card]] in
    var list = accumulator[card.key] ?? [Card]()
    list.append(card)
    accumulator[card.key] = list
    return accumulator
}

Hope this helps.

1 Comment

Great points, thanks! Actually I didn't have the "lets" in my code, I just made an error here. The same thing happened with the array of cards, it's named correctly in my code. To your solution - it's great, but I think I prefer the Eric's one. It's good to see it from a different angle though.
0

Array in Swift now is Struct, because this it is always copy.

For your solution I recommend do it.

Mind you get a copy of Array and after change you commit your edit back to Dictionary.

let turnsWithCardsPlayed = [String: [Card]]()
for card in arrayOfCards {
    //verify if have cards in dictionary and get it
    if var cards = turnsWithCardsPlayed["Turn " + card.turn] {
        //append element card in copied array
        cards.append(card)
    }else{ 
        // if no has array create new and append new card
        cards = []
        cards.append(card)
    }
    //commit in dictionary your new or modified array
    turnsWithCardsPlayed["Turn " + card.turn] = cards
}

1 Comment

Thanks for answer. That doesn't really simplify my solution though.
0

Similar to your solution, but using a ternary conditional instead and then pulling out the repeated append of the card, since it will be added regardless of if an array is needed to be added to the dictionary or not.

let turnsWithCardsPlayed = [String: [Card]]()
for card in arrayOfCards {
    turnsWithCardsPlayed["Turn " + card.turn] != nil ? () : turnsWithCardsPlayed["Turn " + card.turn] = []
    turnsWithCardsPlayed["Turn " + card.turn]!.append(card)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.