Search code examples
arraysswiftcounting

Counting number of elements in an array of array of struct in Swift 5


I'm really struggling as newbie with how to solve a problem of counting elements in an array of an array of structs. I've tried several different methods but it isn't giving me the results I need.

So I have a Struct called Card defined as follows:

 struct Card: Hashable {
    
    var dateGroup: DateGroup
    var countryGroup: CountryGroup
    var icon1: IconGroup
    var icon2: IconGroup
    
    init(dateGroup: DateGroup, countryGroup: CountryGroup, icon1: IconGroup, icon2: IconGroup) {
        
        self.dateGroup = dateGroup
        self.countryGroup = countryGroup
        self.icon1 = icon1
        self.icon2 = icon2
   
    } 
}

For reference, the elements of Card are defined as enums using the following code:

enum DateGroup: String {
    
    case DG1, DG2, DG3
    
    static let allValues = [DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG1,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG2,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3, DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3,    DG3]
    
}

enum CountryGroup: String {
    
    case IT, NE, FS, US
    
    static let allValues = [IT,    IT,    IT,    IT,    IT,    IT,    IT,    IT,    NE,     NE,     IT,    NE,     NE,     NE,     IT,    IT,    IT,    NE,     IT,    NE,     IT,    IT,    IT,    NE,     IT,    IT,    IT,    IT,    NE,     IT,    NE,     NE,     FS,    FS,    NE,     IT,    IT,    NE,     NE,     IT,    IT,    NE,     FS,    FS,    FS,    NE,     FS,    NE,     NE,     NE,     NE,     FS,    NE,     NE,     FS,    NE,     IT,    NE,     NE,     FS,    FS,    NE,     IT,    NE,     FS,    NE,     NE,     NE,     NE,     FS,    FS,    FS,    FS,    FS,    US,    FS,    FS,    FS,    FS,    NE,     FS,    FS,    FS,    NE,     US,    IT]
    
}

enum IconGroup: String {
    
    case Portrait, People, Still, Scape, Setting
    
    static let allValues1 = [Portrait,     People,    People,    Portrait,     Portrait,     People,    People,    People,    People,    Portrait,     People,    Portrait,     People,    People,    People,    Portrait,     People,    Portrait,     People,    Portrait,     Portrait,     People,    People,    Portrait,     People,    People,    Portrait,     People,    Portrait,     People,    Portrait,     People,    Portrait,     People,    People,    People,    People,    Still,    People,    Portrait,     People,    Portrait,     Still,    People,    People,    People,    Portrait,     Still,    People,    Portrait,     Portrait,     People,    Still,    Still,    People,    Still,    People,    Portrait,     Portrait,     People,    People,    People,    People,    Portrait,     Portrait,     People,    Scape,    Scape,    Setting,    People,    People,    Still,    People,    Still,    Setting,    Scape,    People,    People,    People,    Still,    Still,    Portrait,     People,    Portrait,     People,    People]
    
    static let allValues2 = [Setting,    Setting,    Setting,    Setting,    Setting,    Setting,    Setting,    Scape,    Setting,    Setting,    Scape,    Scape,    Setting,    Setting,    Scape,    Scape,    Setting,    Scape,    Setting,    Scape,    Scape,    Scape,    Scape,    Setting,    Scape,    Scape,    Setting,    Scape,    Setting,    Setting,    Portrait,     Setting,    Setting,    Setting,    Scape,    Portrait,     Setting,    Still,    Scape,    Setting,    Scape,    Setting,    Still,    Scape,    Scape,    Scape,    Setting,    Still,    Scape,    Portrait,     Setting,    Setting,    Still,    Still,    Scape,    Still,    Scape,    Portrait,     Scape,    Setting,    Scape,    Setting,    Scape,    Setting,    Setting,    Scape,    Scape,    Scape,    Scape,    Scape,    Scape,    Still,    Scape,    Still,    Still,    Scape,    Setting,    Setting,    Scape,    Still,    Still,    Setting,    Setting,    Setting,    Setting,    Setting]
    
}

I've created a class called DeckOfCards and functions to shuffle the deck and deal a hand to a defined number of players. I've eventually got all that to work and checked by printing results in the console and it all checks out.

So each player has say 10 cards and is defined as an array of type Card (and as an aside the players are also stored in an array called newGame). So newGame[0] is player 1 and newGame[0][0] is the first card in the players hand. And therefore I can reference the dateGroup of that card by newgame[0][0].dateGroup for example.

What I'm trying to achieve is checking if a player has a set of 5 or more cards where two of the card elements are the same. I've started by trying to do a count of the card elements in a players hand. So for example how may of the cards have dateGroup of DG1, how many are DG2 etc, how many are countryGroup IT etc etc.

I've tried to do use joined() and then do a count but that didn't work. I'm thinking of returning an array with a count by each possible element type and have started with this very skeletal code:

  func createIconCount(playerHand: [Card] ) -> [String : Int] {
        
        var counts: [String: Int] = [:]

        //insert counting code here

        return counts
    }

So I'd get an array back with something like [DG1: 3, DG2: 3, ....... IT: 4, NE: 5, .... Portrait: 6, People: 4, .....]

Help! Any ideas?

Rob


Solution

  • Use the rawValue of the attributes and a default total of 0 on dictionary lookup to total the values:

    func createIconCount(playerHand: [Card] ) -> [String : Int] {
        
        var counts: [String: Int] = [:]
    
        for card in playerHand {
            counts[card.dateGroup.rawValue, default: 0] += 1
            counts[card.countryGroup.rawValue, default: 0] += 1
            counts[card.icon1.rawValue, default: 0] += 1
            // Don't count icon2 if it is the same as icon1
            if card.icon2 != card.icon1 {
                counts[card.icon2.rawValue, default: 0] += 1
            }
        }
    
        return counts
    }
    

    Creating an array of indices of cards with icon:

    Instead of creating a count of the cards with a matching icon, you can create an array of the indices of the cards in the player's hand. Start by adding .enumerated to playerHand to get the indices, and then append the idx to the array. Here we use [] as the default dictionary lookup value creating an empty array if one doesn't already exist.

    func createIconCount(playerHand: [Card] ) -> [String : [Int]] {
        
        var counts: [String: [Int]] = [:]
    
        for (idx, card) in playerHand.enumerated() {
            counts[card.dateGroup.rawValue, default: []].append(idx)
            counts[card.countryGroup.rawValue, default: []].append(idx)
            counts[card.icon1.rawValue, default: []].append(idx)
            // Don't count icon2 if it is the same as icon1
            if card.icon2 != card.icon1 {
                counts[card.icon2.rawValue, default: []].append(idx)
            }
        }
    
        return counts
    }