0

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

2
  • To clarify, if a player has 3 cards with (DG1, IT), (DG1, NE) and (DG2, IT) then the count of matching cards (2 elements are the same) is zero? (I am skipping icon group for brevity). Also are icon1 and icon2 to be compared separately ,that is if a player has 2 cards where icon1 = People and for the other card icon2 = People then is that a match or does it have to be icon1 = icon1 for different cards? Commented Jul 29, 2020 at 17:19
  • Hi, think I've got a solution now. Your interpretation is correct - no matching pairs in your example. I create an array of the indices per icon and can then look to see if two indices are the same then those 2 cards have a matching pair. So if I get these two arrays back [0, 2,3, 6, 7, 8] and [0, 1, 2, 3, 6, 7] then I have 5 cards with matching pairs (or a collection as per the rules of my game). Thanks. Just need to write the logic now to compare the arrays and declare if a player has a collection or not - at the moment I'm just outputting the arrays and looking at them. Commented Aug 1, 2020 at 12:01

2 Answers 2

2

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
}
Sign up to request clarification or add additional context in comments.

4 Comments

Can a card have the same value for icon1 and icon2? For example can a card be (DG1, IT, Portrait, Portrait)? If so, how you would want to count Portrait for that card? Once or twice?
Hi, icon 1 and icon 2 can be the same. In that case it would count as one icon - it would have to match an icon on another card which could be portrait and setting for example.
Hi, as an aside, how easy would it be to return an array (or possible an array of array?) showing the indices for each of the counted items e.g. if DG1 appears 4 times in the playerHand and it appeared at [0, 3, 4, 7]? Thanks in advance. Rob
Hi, just tried the indices code and it works brilliantly! It is so elegant, thank you. I hadn't realised you could do a for loop with two bits (i.e. for (idx, card) in ) - that's really useful and helpful. My attempt would have been much clunkier!!
0

Here is the function you are looking for:

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

    //insert counting code here
    //=========================
    for player in playerHand {
        if(!counts.keys.contains(player.dateGroup.rawValue)) {
            counts[player.dateGroup.rawValue] = 0;
        }
        counts[player.dateGroup.rawValue]! += 1;
    }
    //=========================

    return counts
}

2 Comments

Thank you both - I hadn't come across rawValue before.
Hi, tried both solutions posted. Unfortunately, the second one comes back with "Value of type 'Card' has no member 'rawValue'" but the first solution gives me an array with the counts. Thanks for the help - learning something new every day!

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.