# Tough List Comprehension Split Letters and Numbers

So, another challenge from my professor. I've finished it, but wanted to get some feedback, and see how other people might work this problem.

Assume you have a string, such as "Hello 34215 World 5620 From 384 Bloomington"

Use list comprehension to separate the words from the numbers, and the numbers from the words. Then order the numbers from smallest to largest. Print out these words and numbers separated by a "|". The output for the example given above would be:

>>>
Hello World From Bloomington | 012233445568


The challenge is to write the code in two lines, one of the lines being the definition of the string.

This is what I wrote:

string = "Hello 34215 World 5620 From 384 Bloomington"
print str(" | ".join(["".join([letter for letter in string if letter.isalpha() or letter.isspace()]), "".join(sorted([num for num in string if num.isdigit()]))])).replace("  ", " ")


It works! The output is identical to the output displayed. But, I'd love to hear feedback about how this is done, or if there are any pieces of redundant code, etc. I don't use lambda, map, set, etc. nor do I import any libraries, but if you find that there is a way to do it with any of those pieces, feel free to share!

• "".join(x for x in s if not x.isdigit()) +" | "+ "".join(sorted(x for x in s if x.isdigit() )) Jun 30, 2014 at 23:42
• @Padraic Cunningham The issue with that line is that there are two spaces between each word. I found I had that issue before, and had to change some of my code to fix it. Jun 30, 2014 at 23:45
• " ".join(x for x in s.split() if not x.isdigit()) +" | "+ "".join(sorted(x for x in s if x.isdigit() )) Jul 1, 2014 at 0:01
• From the description, I really would have expected the right output to be Hello World From Bloomington | 384 5620 34215. Jul 1, 2014 at 3:18
• @user2357112 The description is paraphrased. Jul 1, 2014 at 17:03

Padraic Cunningham nailed the shortest form in his comment:

" ".join(x for x in s.split() if not x.isdigit()) + " | " + "".join(sorted(x for x in s if x.isdigit()))


Explanation:

• x for x in s.split() if not x.isdigit() -- the list of words that don't have digits. Note that the split method without parameters uses space as the separator.
• x for x in s if x.isdigit() -- the list of digits extracted from the string, unsorted
• From the above 2 it's easy to combine to get the desired result.

Although this is short, it checks every letter twice, first using not x.isdigit() to get only the letters and then x.isdigit() to get only the digits. We could improve that by using a helper function to partition the list of words on the outcome of x.isdigit(), like this:

def partition(pred, iterable):
trues = []
falses = []
for item in iterable:
if pred(item):
trues.append(item)
else:
falses.append(item)
return trues, falses

nums, words = partition(str.isdigit, s.split())
' '.join(words) + ' | ' + ''.join(sorted(''.join(nums)))


Of course, this is much more than one line...