RoadRunner
RoadRunner

Reputation: 26325

sorting list of cards

I have two lists:

card_candidates = ['9D', '9S', '3S', '0D']
card_order = ['2', '3', '4', '5', '6', '7', '8', '9', '0', 'J', 'Q', 'K', 'A']

I want to be able to sort the first list with respect to the second lists order. So the sorted card_candidates should look like this:

sorted_candidates = ['3S', '9D', '9S', '0D']

The '0' is just the value of 10, just wanted to make all the cards the same length. If there is a tie, like with '9D' and '9S', then the letters will need to be sorted instead. So far I have just done this:

sorted_candidates = []
for x, y in zip(card_candidates, card_order):
    sorted_candidates.append([x[0] for x in card_candidates])
return sorted(sorted_candidates)

I know that this is not even close to being right, I just don't know how to do this.

Upvotes: 1

Views: 2210

Answers (2)

Byte Commander
Byte Commander

Reputation: 6756

You don't necessarily need any additional data structures, the card_order list is totally enough to sort your cards using this simple one-liner:

card_candidates = ['9D', '9S', '3S', '0D']
card_order = ['2', '3', '4', '5', '6', '7', '8', '9', '0', 'J', 'Q', 'K', 'A']

sorted_cards = sorted(card_candidates, key=lambda c: (card_order.index(c[0]), c[1]))

print(list(sorted_cards))

Output:

['3S', '9D', '9S', '0D']

See this code running on ideone.com

Please note that this approach scans the card_order list once for every card to sort each time you sort any card list. This is no problem if you just sort a normal deck of cards at the beginning of a game or even every few seconds. However, if your code is sorting huge numbers of cards or sorting cards most of its time, you might want to prefer the solution by Martijn Pieters which converts the card_order list into a dictionary that can be scanned faster.

Upvotes: 2

Martijn Pieters
Martijn Pieters

Reputation: 1124040

Produce a dictionary mapping your sort order characters to a number, we'll sort by those:

sort_map = {c: i for i, c in enumerate(card_order)}

You can now use this mapping to sort your cards, including the second letter to break ties:

sorted_candidates = sorted(card_candidates,
                           key=lambda card: (sort_map[card[0]], card[1]))

The sort key takes the first character of each card and translates that to an integer from the sort_map dictionary, which then inform the sorted() function of the correct sort order. In case of a tie (equal card value), cards are sorted in clubs, diamonds, hearts, spades order (assuming you use C and H for clubs and hearts).

Creating a mapping up front keeps the sort within O(NlogN) time complexity; you could do the same with card_order.index(card[0]), but then you invoke a list.index() call for every sorted element, and list.index needs to scan the list, producing a O(KNlogN) sort (where K is the length of the card_order list).

Demo:

>>> card_candidates = ['9D', '9S', '3S', '0D']
>>> card_order = ['2', '3', '4', '5', '6', '7', '8', '9', '0', 'J', 'Q', 'K', 'A']
>>> sort_map = {c: i for i, c in enumerate(card_order)}
>>> sorted(card_candidates, key=lambda card: (sort_map[card[0]], card[1]))
['3S', '9D', '9S', '0D']
>>> sorted(['9D', '9S', '9C', '9H'], key=lambda card: (sort_map[card[0]], card[1]))
['9C', '9D', '9H', '9S']

Upvotes: 11

Related Questions