0

I have no idea where to start with this. I need to write a function that will return a string of numbers in ordinal value. So like

stringConvert('DABC')

would give me '4123'

stringConvert('XPFT')

would give me '4213'

I thought maybe I could make a dictionary and make the each letter from the string associate it with an integer, but that seems too inefficient and I still don't know how to put them in order.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Eddie
  • 63
  • 2
  • 5

3 Answers3

2

You could sort the unique characters in the input string and apply indices to each letter by using the enumerate() function:

def stringConvert(s):
    ordinals = {c: str(ordinal) for ordinal, c in enumerate(sorted(set(s)), 1)}
    return ''.join([ordinals[c] for c in s])

The second argument to enumerate() is the integer at which to start counting; since your ordinals start at 1 you use that as the starting value rather than 0. set() gives us the unique values only.

ordinals then is a dictionary mapping character to an integer, in alphabetical order.

Demo:

>>> def stringConvert(s):
...     ordinals = {c: str(ordinal) for ordinal, c in enumerate(sorted(set(s)), 1)}
...     return ''.join([ordinals[c] for c in s])
... 
>>> stringConvert('DABC')
'4123'
>>> stringConvert('XPFT')
'4213'

Breaking that all down a little:

>>> s = 'XPFT'
>>> set(s)  # unique characters
set(['X', 'F', 'T', 'P'])
>>> sorted(set(s))  # unique characters in sorted order
['F', 'P', 'T', 'X']
>>> list(enumerate(sorted(set(s)), 1))  # unique characters in sorted order with index
[(1, 'F'), (2, 'P'), (3, 'T'), (4, 'X')]
>>> {c: str(ordinal) for ordinal, c in enumerate(sorted(s), 1)}  # character to number
{'P': '2', 'T': '3', 'X': '4', 'F': '1'}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • You can pass the generator directly to `str.join`. No need to create the list first. I.e., `''.join(ordinals[c] for c in s)` – Peter Sutton Feb 04 '15 at 14:12
  • @PeterSutton: see [this answer](http://stackoverflow.com/a/9061024); `str.join()` is the **one** place where using a list comprehension is going to be more efficient; to join the string the iterable has to be converted to a list otherwise anyway so as to scan it twice. – Martijn Pieters Feb 04 '15 at 14:14
  • Hi, thanks for the quick response. But can you explain to me what you did here. I'm fairly new to python and this is the first time I encountered the enumerate and sorted sets part. – Eddie Feb 04 '15 at 14:24
  • @EdwardStark: try out the different parts in the interpreter. `set()` produces a collection of letters that is unique; so `set('aaaaaa')` gives you `set(['a'])`, just the one letter `a`, etc. `sorted()` produces a new list with values from the input, sorted. – Martijn Pieters Feb 04 '15 at 14:26
  • @EdwardStark: `enumerate()` produces `(index, value)` tuples as you iterate. So for the input `'a', 'b', 'c', 'd'` the output `(1, 'a')`, `(2, 'b')`, `(3, 'c')`, `(4, 'd')` is produced. The dictionary comprehension takes those pairs and maps the character to the number (converted to a string here because that's the output you wanted). – Martijn Pieters Feb 04 '15 at 14:27
1

Take a look at string module, especially maketrans and translate

With those, your code may look like

def stringConvert(letters):
    return translate(letters, maketrans(''.join(sorted(set(letters))).ljust(9), '123456789'))

and pass your strings as variable

Slam
  • 8,112
  • 1
  • 36
  • 44
1

You could make a character translation table and use the translate() string method:

from string import maketrans

TO = ''.join(str(i+1)[0] for i in xrange(256))

def stringConvert(s):
    frm = ''.join(sorted(set(s)))
    return s.translate(maketrans(frm, TO[:len(frm)]))

print stringConvert('DABC')  # --> 4123
print stringConvert('XPFT')  # --> 4213
martineau
  • 119,623
  • 25
  • 170
  • 301
  • Because you are already heavily leaning on `sorted()` and using `str.join()`, any advantage `str.translate()` may have had has evaporated. – Martijn Pieters Feb 04 '15 at 14:31
  • @Martijn: Good point, but moving the creation of the translation's "TO" string outside of the function -- since its a constant except for its length -- makes up for that and the result is faster than your version in a benchmark I setup and ran: `martineau : 2.808794 secs, rel speed 1.000000x` vs `Martijn Pieters : 5.143616 secs, rel speed 1.831254x` for 1,000,000 calls each, best of 3. – martineau Feb 04 '15 at 15:20
  • Yup, you are now using the same approach as Slam; predefine the target mapping. – Martijn Pieters Feb 04 '15 at 15:35