Is there a way to subtract 3 from every digit of a number?

Terry Reedy tjreedy at udel.edu
Sat Feb 20 13:51:56 EST 2021


On 2/20/2021 12:02 PM, jak wrote:
> Il 20/02/2021 15:40, C W ha scritto:
>> Hello everyone,
>>
>> I'm curious if there is a way take number and back each digit by 3 ?
>>
>> 2342 becomes 9019
>> 8475 becomes 5142
>> 5873 becomes 2540
>>
>> The tricky part is that 2 becomes 9, not -1.
>>
>> Here's my toy example and what I attempted,
>>> test_series = pd.Series(list(['2342', '8475', '5873']))
>>> test_series
>> 0    2342
>> 1    8475
>> 2    5873
>> dtype: object
>>
>>> test_series.str.split('')
>> [, 2, 3, 4, 2, ]
>> [, 8, 4, 7, 5, ]
>> [, 5, 8, 7, 3, ]
>> dtype: object
>>
>> What a good approach to this? Is there a method or function that 
>> should be
>> handling this?

MRAB gave the proper answer -- str.translate (and str.maketrans.

>  >>> num='0123456789'
>  >>> n=8475
>  >>> sn = ''
>  >>> for x in str(n):
>        sn += num[(int(x) - 3) % 10]

 >  >>> int(sn)
 > 5142

This works, but suggesting to beginners that they build strings with += 
is an O(n*n) trap. Try it with a string of millions of digits.  Much 
better to use str.join

''.join(num[(int(c)-3) % 10] for c in '9876543210')
#'6543210987'

Even better, improve your string lookup idea to avoid the arithmetic.

lookup1 = '7890123456'
''.join(lookup1[int(c)] for c in '9876543210')
#'6543210987'

To avoid the int call, make a lookup dictionary

lookup2 = {a:b for a, b in zip('0123456789', '7890123456')}
''.join(lookup2[c] for c in '9876543210')
#'6543210987'

To run faster, use the str methods maketrans and  translate to do the 
same thing.

lookup3 = str.maketrans('0123456789', '7890123456')
'9876543210'.translate(lookup3)
#'6543210987'

Note that "built-in function" str.maketrans is in effect a static method 
of the str class and is not an instance method. 
'0123456789'.maketrans('7890123456') does not work.  The reason is that 
the first argument can be a dict instead of a string.  Indeed, the dict 
could be the char-char mapping above created with the dict comprehension.

Also, the resulting dict maps unicode ordinals to unicode ordinals, 
rather than chars to chars, because at the C level, a string *is* a 
sequence of unsigned ints with a PyObject wrapper.

 >>> lookup3
{48: 55, 49: 56, 50: 57, 51: 48, 52: 49, 53: 50, 54: 51, 55: 52, 56: 53, 
57: 54}

In Python, this is
{ord(a):ord(b) for a, b in zip('0123456789', '7890123456')}

-- 
Terry Jan Reedy




More information about the Python-list mailing list