Eugleo
Eugleo

Reputation: 428

Swift: Adding an element into array contained in a dictionary

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 :)

Upvotes: 2

Views: 141

Answers (4)

Luca Angeletti
Luca Angeletti

Reputation: 59496

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.

Upvotes: 1

Matt Le Fleur
Matt Le Fleur

Reputation: 2856

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)
}

Upvotes: 0

Eric Aya
Eric Aya

Reputation: 70098

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)
}

Upvotes: 2

ViTUu
ViTUu

Reputation: 1204

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
}

Upvotes: 0

Related Questions