-4

How would I do this?

Write a Python program that meets the following requirements:

It prompts the user for six alphanumeric characters (A-Z, 0-9) separated by spaces. It sorts the user input in ascending order, letters first, then numbers. It prints the list of sorted characters to the screen (separated by spaces). It is well commented.

Example:

If the program's input is 8 G J 4 5 D, the output would be D G J 4 5 8

I wrote a program, but when inputing data that had only numbers, it would give me an error. Any help would be appreciated.

Pythoneer
  • 319
  • 1
  • 16
Chroma
  • 11

3 Answers3

2

Sort with a key so that decimal characters go before letters:

>>> s = "8 G J 4 5 D"
>>> print(*sorted(s.split(), key=lambda c: (c.isdecimal(), c)))
D G J 4 5 8
Samwise
  • 68,105
  • 3
  • 30
  • 44
  • 3
    It is clever how you used the lex ordering of tuples in your key. It took me a couple of minutes to fully understand how it works. – John Coleman Dec 09 '22 at 19:53
  • @Samwise, what do you think of my lookup solution? – Nineteendo Dec 09 '22 at 20:06
  • @Samwise I don't understand the trick - could you, er, unpack it a bit? – jtlz2 Dec 09 '22 at 20:10
  • 2
    @jtlz2: tuple comparison works about the same as string comparison (a "lexicographical comparison" sort of thing): you start with comparing the elements in the first index. If one is greater, then the tuple it comes from is greater. Else, they're equal: move on to the next index. (There's also the matter of varying lengths, but it is not relevant here.) In the case of this answer, the first element is a Boolean: True if c is a decimal, False otherwise; this means that numbers would be "larger" because their first element is True, while for non-numbers it's False (and True>False) – Shay Dec 09 '22 at 20:33
  • @Nineteendo if I had a huge number of lists to sort like this rather than building my own table I might just wrap the sort function in a `functools.cache`. Not positive whether it’d be the same speed up but it should have roughly the same effect and it’d make the code a lot simpler (and simpler means fewer opportunities for bugs when you need to extend it). – Samwise Dec 09 '22 at 20:54
  • @Samwise, I've simplified the code and made it work as I intended. The lookup table is now a one liner. – Nineteendo Dec 09 '22 at 21:32
1

If you want your code to be almost two times faster, you can use a lookup table, I've used a one liner for this, but it can also be split up. I'm printing this to provide insight in what this object looks like. For the numbers I can make a list directly, for the letters I'll use a more compact notation.

decode = {character: index for index, character in enumerate([chr(i) for i in range(ord("A"), ord("Z") + 1)] + ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"])}
print(decode)

s = "8 G J 4 5 D"
print(*sorted(s.split(), key=lambda c: (c.isdecimal(), c)))
print(*sorted(s.split(), key = lambda c: decode[c]))

from timeit import repeat
loops = 500_000
count = 1
print(loops * min(repeat("sorted(s.split(), key=lambda c: (c.isdecimal(), c))", globals=globals(), repeat=loops, number=count)))
print(loops * min(repeat("sorted(s.split(), key = lambda c: decode[c])", globals=globals(), repeat=loops, number=count)))

Output:

{'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25, '0': 26, '1': 27, '2': 28, '3': 29, '4': 30, '5': 31, '6': 32, '7': 33, '8': 34, '9': 35}
D G J 4 5 8
D G J 4 5 8
3.6460114642977715
2.3174798116087914

More ideas?

Nineteendo
  • 882
  • 3
  • 18
-1
# NOTE: This code is definitely not the most efficient.
import re  # Import the regex standard library to check the input later

input_ = input("Enter six alphanumeric characters, separated by spaces: ")  # Get the input

# Check if the input is six alphanumeric characters, separated by spaces.
# [A-Z0-9] in regex matches any character in the A-Z (capitalized) or 0-9 range
# ^ in regex matches the BEGINNING of the string
# $ in regex matches the END of the string
if not re.match(r"^[A-Z0-9] [A-Z0-9] [A-Z0-9] [A-Z0-9] [A-Z0-9] [A-Z0-9]$", input_):  # If the string does not match the pattern (that specifies for the aforesaid criteria)
  # Exit the program, printing "Invalid input"
  print("Invalid input")
  exit()


numbers_sorted = []  # Create a list that will later be populated with all the numbers in the input, sorted.
letters_sorted = []  # Create a list that will later be populated with all the Letters in the input, sorted.


# Loop through each character in the input.
# .split() splits the input into a list at every space (e.g. "a b c d e f".split() becomes ["a", "b", "c", "d", "e", "f"])
for character in input_.split(): 
  if character.isalpha():  # If the character is alphabetic
    # Sort the character into the `letters_sorted` list

    # Loop through the length of the list, such that the code in the loop is executed n times,
    # where n is the length of `letters_sorted`,
    # and the `index` variable starts at 0, and increases per iteration.
    for index in range(len(letters_sorted)):
      # ord() returns the 'character code' of a character (e.g. ord('a') returns 97, ord('b') returns 98)
      # If the `character` (from the outer for loop) is alphabetically preceeding the
      # character at position `index` of the `letters_sorted` list,
      if ord(letters_sorted[index]) > ord(character):
        letters_sorted.insert(index, character)  # Insert the `character` (from the outer loop) right before `index` of `letters_sorted`
        break  # Break from the loop as the character has been sorted into the list
    else:
      # If the character has not been sorted into the list
      # (if the character alphabetically succeeds every other character currently in `letters_sorted`)
      letters_sorted.append(character)  # Append the character to the very end of the list
  else:  # Otherwise (in this case, if the character is numeric)
    # Sort the character into the `numbers_sorted` list
    # See the comments above for sorting alphabetic characters
    # The only difference is not using the ord() function as we can directly compare numbers using less-than or greater-than
    # (also, we are using the `numbers_sorted` list now)
    for index in range(len(numbers_sorted)):
      if numbers_sorted[index] > character:
        numbers_sorted.insert(index, character)
        break
    else:
      numbers_sorted.append(character)

# Now, the lists are 'in order'.
# Finally, combine the lists to achieve a final list that contains letters sorted, then numbers sorted.
final_list = letters_sorted + numbers_sorted

# (Very) finally, convert the list to a string, separating each list entry by a space character:
final_string = " ".join(final_list)
# (Very very) finally, print the string:
print(final_string)

EDIT: Please any of the other answers above, all of them are much more concise than this one

Daniel M
  • 16
  • 3