Search code examples
pythonlist-comprehensionplaying-cards

Create deck of multiple decks of cards using list comprehension


I've created a Card object:

class Card:
    def __init__(self, rank, suit, d=0): 
        self.rank = rank
        self.suit = suit
        self.deck_index = d

I can make a list of Cards that represents a standard deck, using list comprehension:

ranks = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A']
suits = ['hearts', 'clubs', 'diamonds', 'spades']
deck = ([Card(r, s) for r in ranks for s in suits] + 
        [Card('Joker', 'black'), Card('Joker', 'red')])

I'd like to create a list of Cards that represents N decks combined, using list comprehension.


I can do it with a for loop:

decks = []
for i in range(N):
    decks += ([Card(r, s, d=i) for r in ranks for s in suits] +
              [Card('Joker', ' black', i), Card('Joker', 'red', i)])

But if I write this loop as a list comprehension, I get a list of lists, instead of a list of cards:

decks = [[Card(r, s, d=i) for r in ranks for s in suits] + 
         [Card('Joker', 'black', i), Card('Joker', 'red', i)] 
         for i in range(N)]

I tried putting the target in parentheses and unpacking it with *. I got the error: Iterable unpacking cannot be used in comprehension

I could use list comprehension for the deck without Jokers and add the Jokers separately with their own list comprehensions:

decks = ([Card(r, s, i) for r in ranks for s in suits for i in range(N)] + 
         [Card('Joker', 'black', i) for i in range(N)] + 
         [Card('Joker', 'red', i) for i in range(N)])

But this redundantly specifies the loop three times.

It also puts Jokers at the end, instead of preserving the original order with Jokers next to the cards from their own deck. This point isn't too important because I shuffle in my next step, but I would like to know if it's possible to combine the decks with order preserved using list comprehension.


I'd state the general problem like this:

lst = lst1 + lst2 is a list of elements

lsts = [lst1(i) + lst2(i) for i in range(N)] is a list of lists

How can I use list comprehension to create a list of elements in lst1(i) + lst2(i) for i in range(N)?

The solution's likely available online but I don't know what words to search for. "Combine sums of lists using list comprehension" gives list comprehension tutorials and how to sum lists elementwise. One option I found is to let it make a list of lists and then flatten that using sum(lst,[]) (slow) or list(itertools.chain.from_iterable(lst)) (fast). But can I avoid making a list of lists?


Solution

  • This creates the same as your "for loop" solution:

    decks = [
        Card(r, s, i)
        for i in range(N)
        for rs, ss in [
            (ranks, suits),
            (['Joker'], ['black', 'red'])
        ]
        for r in rs for s in ss
    ]
    

    Attempt This Online!

    Since my list with the two "product specifications" is the same for every i, it could be built once beforehand and (re)used for each i. But it's relatively small compared to the overall work/output, so I kept it simple.