Home > Software engineering >  Why is sort() function not properly working with the integers in this list?
Why is sort() function not properly working with the integers in this list?

Time:11-24

I am trying to create a deck of cards, shuffle it, and then re-sort it.

The problem is when I sort it after shuffling, the list ends up like this: [10, 11, 12, 13, 8, 9]

I used a smaller deck for example. With a full deck the list still ends up: [10, 11, 12, 13, 1, 2, ...]

Why is the list not properly being sorted?

Here's my code:

import random

# Initialize Deck
deck = []
suits = ['Club', 'Spade', 'Heart', 'Diamond']

for i in range(8, 14):
    for s in range(4):
        deck.append([str(i), suits[s]])

# Format numbers into card face
for i in range(len(deck)):
    if deck[i][0] == "1":
        deck[i][0] = "Ace"
    elif deck[i][0] == "11":
        deck[i][0] = "J"
    elif deck[i][0] == "12":
        deck[i][0] = "Q"
    elif deck[i][0] == "13":
        deck[i][0] = "K"

print("Initial deck", deck)
print()

# Shuffle Deck
random.shuffle(deck)
print("Shuffle function:", deck)

# Turn face back to number for sorting
for i in range(len(deck)):
    if deck[i][0] == "Ace":
        deck[i][0] = "1"
    elif deck[i][0] == "J":
        deck[i][0] = "11"
    elif deck[i][0] == "Q":
        deck[i][0] = "12"
    elif deck[i][0] == "K":
        deck[i][0] = "13"
        
deck.sort()
print("Sort function: ", deck)

Output:

Initial deck: [['8', 'Club'], ['8', 'Spade'], ['8', 'Heart'], ['8', 'Diamond'], ['9', 'Club'], ['9', 'Spade'], ['9', 'Heart'], ['9', 'Diamond'], ['10', 'Club'], ['10', 'Spade'], ['10', 'Heart'], ['10', 'Diamond'], ['J', 'Club'], ['J', 'Spade'], ['J', 'Heart'], ['J', 'Diamond'], ['Q', 'Club'], ['Q', 'Spade'], ['Q', 'Heart'], ['Q', 'Diamond'], ['K', 'Club'], ['K', 'Spade'], ['K', 'Heart'], ['K', 'Diamond']]

Shuffle function: [['J', 'Heart'], ['8', 'Club'], ['Q', 'Spade'], ['9', 'Diamond'], ['9', 'Heart'], ['10', 'Diamond'], ['K', 'Heart'], ['K', 'Spade'], ['10', 'Heart'], ['9', 'Club'], ['8', 'Heart'], ['K', 'Club'], ['8', 'Diamond'], ['Q', 'Diamond'], ['9', 'Spade'], ['Q', 'Heart'], ['10', 'Spade'], ['K', 'Diamond'], ['J', 'Diamond'], ['Q', 'Club'], ['10', 'Club'], ['8', 'Spade'], ['J', 'Spade'], ['J', 'Club']]

Sort function:  [['10', 'Club'], ['10', 'Diamond'], ['10', 'Heart'], ['10', 'Spade'], ['11', 'Club'], ['11', 'Diamond'], ['11', 'Heart'], ['11', 'Spade'], ['12', 'Club'], ['12', 'Diamond'], ['12', 'Heart'], ['12', 'Spade'], ['13', 'Club'], ['13', 'Diamond'], ['13', 'Heart'], ['13', 'Spade'], ['8', 'Club'], ['8', 'Diamond'], ['8', 'Heart'], ['8', 'Spade'], ['9', 'Club'], ['9', 'Diamond'], ['9', 'Heart'], ['9', 'Spade']]

CodePudding user response:

This is because the values in your list are lists, and those lists contain strings. Try something like this:

import random

# Initialize Deck
deck = []
suits = ['Club', 'Spade', 'Heart', 'Diamond']

for i in range(8, 14):
    for s in range(4):
        deck.append([i, suits[s]])

# Format numbers into card face
for i in range(len(deck)):
    if deck[i][0] == 1:
        deck[i][0] = "Ace"
    elif deck[i][0] == 11:
        deck[i][0] = "J"
    elif deck[i][0] == 12:
        deck[i][0] = "Q"
    elif deck[i][0] == 13:
        deck[i][0] = "K"

print("Initial deck", deck)
print()

# Shuffle Deck
random.shuffle(deck)
print("Shuffle function:", deck)

# Turn face back to number for sorting
for i in deck:
    if deck[i][0] == "Ace":
        deck[i][0] = 1
    elif deck[i][0] == "J":
        deck[i][0] = 11
    elif deck[i][0] == "Q":
        deck[i][0] = 12
    elif deck[i][0] == "K":
        deck[i][0] = 13
        
deck.sort(key=lambda x:return x[0])
print("Sort function: ", deck)

CodePudding user response:

You want the ranks to be int values (instead of their str versions) so that they can be compared for sorting. Flipping them back and forth for printing/sorting is a lot of unnecessary work; I'd suggest defining an object class with a __repr__ method that formats them the way you want.

import random


class Card:
    def __init__(self, rank: int, suit: str):
        self.rank = rank
        self.suit = suit

    def __repr__(self) -> str:
        rank_names = {
            1: "Ace",
            11: "J",
            12: "Q",
            13: "K",
        }
        return f"[{rank_names.get(self.rank, self.rank)} {self.suit}]"


deck = [
    Card(rank, suit) 
    for rank in range(8, 14) 
    for suit in ['Club', 'Spade', 'Heart', 'Diamond']
]

print("Initial deck", deck, "\n")

random.shuffle(deck)
print("Shuffled deck:", deck, "\n")

deck.sort(key=lambda card: (card.rank, card.suit))
print("Sorted deck:", deck)

prints:

Initial deck [[8 Club], [8 Spade], [8 Heart], [8 Diamond], [9 Club], [9 Spade], [9 Heart], [9 Diamond], [10 Club], [10 Spade], [10 Heart], [10 Diamond], [J Club], [J Spade], [J Heart], [J Diamond], [Q Club], [Q Spade], [Q Heart], [Q Diamond], [K Club], [K Spade], [K Heart], [K Diamond]]

Shuffled deck: [[K Heart], [10 Club], [9 Diamond], [8 Diamond], [10 Spade], [J Spade], [K Spade], [J Heart], [9 Spade], [10 Heart], [K Club], [10 Diamond], [Q Club], [K Diamond], [Q Spade], [J Diamond], [Q Diamond], [J Club], [9 Club], [8 Club], [9 Heart], [8 Heart], [Q Heart], [8 Spade]]

Sorted deck: [[8 Club], [8 Diamond], [8 Heart], [8 Spade], [9 Club], [9 Diamond], [9 Heart], [9 Spade], [10 Club], [10 Diamond], [10 Heart], [10 Spade], [J Club], [J Diamond], [J Heart], [J Spade], [Q Club], [Q Diamond], [Q Heart], [Q Spade], [K Club], [K Diamond], [K Heart], [K Spade]]

CodePudding user response:

You should use integers instead of strings for the numeric values. I would also recommend you to replace inner list of two elements with the tuple like (more of a stylistic choice):

[(8, 'Club'), (8, 'Spade'), ...]

If you insist on using strings, then you can add key argument in sort function. It accepts a function that maps your object before comparison. In your case:

deck.sort(key=lambda x: int(x[0]))

which takes each element of the list and converts the first member of this element into an integer.

One other recommendation, use dictionaries instead of if else for mapping between number and card face, like so:

# Dictionary instead of if/elif/else
number_to_face = {
    "1": "Ace",
    "11": "J",
    "12": "Q",
    "13": "K"
}

Here is a full example with the tweaks.

import random

# Initialize Deck
deck = []
suits = ['Club', 'Spade', 'Heart', 'Diamond']

for i in range(8, 14):
    for s in range(4):
        deck.append((str(i), suits[s]))

# Dictionary instead of if/elif/else
number_to_face = {
    "1": "Ace",
    "11": "J",
    "12": "Q",
    "13": "K"
}

# Reverse mapping of key -> value to value -> key
face_to_number = {v: k for k, v in number_to_face.items()}

# Use list comprehension to update list
def map_with(deck, mapping):
    return [(mapping.get(k, k), v) for k, v in deck]

# Format numbers into card face
deck = map_with(deck, number_to_face)

print("Initial deck", deck)
print()

# Shuffle Deck
random.shuffle(deck)
print("Shuffle function:", deck)
print()

# Turn face back to number for sorting
# Use list comprehension to update list
deck = map_with(deck, face_to_number)
        
deck.sort(key=lambda x: int(x[0]))
print("Sort function: ", deck)
  • Related