Write Less Code — The Pythonic Way!

Created on Jul 5, 2021

I had a fruitful discussion with a friend of mine about solving a problem in Python.

The problem we have is like this. We need to convert Western numbers like 1, 2, 3 to their equivalent numbers in Eastern/Indian numbers ١,٢,٣ that we also use in Arabic.

We want a function arabnum to convert western numbers like 42 into their eastern equivalent one ٤٢ like this:

print(arabnum("42"))  # => ٤٢
print(arabnum("953")) # => ٩٥٣

I’ll leave you to solve it.

Brute force logic

Pause ended!

Thinking the C-way and going for the Unicode values for each number. Unicode represents letters of English, Arabic, Indian, etc. It is a superset of ASCII.

But how can we apply the logic here?

We can put two benchmarks here; one for the character 1 and another for the character ١ as two references

The difference between them in Unicode is the number we want to increase to the ASCII of the given western number. Adding this difference will make the western number reach its eastern equivalent.

How in Python

To do that in Python, we use ord ; the function that takes a single character as an input and returns its Unicode code value.

The chr function is the reverse, so its input is the Unicode and the return is the character.

unicode_diff = ord('١') - ord('1')

def arabnum(western_num):
    return chr(unicode_diff + ord(western_num))

print(arabnum('2'))

This code so far helps us find ٢ ; the equivalent eastern number for the number 2 .

But there comes another problem. We want to extend that to multiple digits, not just one digit.

So we need to iterate. A nice way to do it is to use list comprehension.

The result would be a list of converted characters like so:

def arabnum(western_num):
    return [chr(unicode_diff + ord(n)) for n in western_num]

print(arabnum('21'))

and the output here is a list of strings of the two converted numbers: ['٢', '١']

So what’s left is to concatenate such strings using join method:

unicode_diff = ord('١') - ord('1')

def arabnum(western_num):
    return ''.join([chr(unicode_diff + ord(n)) for n in western_num])

print(arabnum('21'))
print(arabnum('953'))

Pythonic solution

Let’s make the code prettier using the standard translate method.

This method takes a mapping dictionary as input. The dictionary’s key should be the Unicode of the number we want to replace (in our case, the western). The associated value to this dictionary is the Unicode value we want to replace with.

unicode_diff = ord('١') - ord('1')

def arabnum(western_num):
    return western_num.translate({ord(n): unicode_diff + ord(n) for n in western_num})

Much cleaner Pythonic solution

Even much easier code for readability and maintainability showing a maketrans method that takes two inputs; the character itself we want to replace and the character we want to replace with. It can also take a bunch of characters mapping each in order with the associated one.

In our case, we put all variance of western numbers and their associated eastern numbers that we want to map to.

The ouput of maketrans method is the mapping input to translate method. See:

def arabnum(western_num):
    t = western_num.maketrans('0123456789', '٠١٢٣٤٥٦٧٨٩')
    return western_num.translate(t)

print(arabnum('953'))

Final thoughts

In this problem-solving session, we saw how to convert Western numerals into Eastern Arabic ones.

We approached this problem with the simplest yet longest solution first using join to concatenate strings and list comprehension. We then went forward to a Pythonic code using a standard method to translate between the two numerals. Until we reached a much more cleaner and Pythonic approach to solve the problem.

Get my next free ebook ‘Disciplined’ once released and more exclusive offers in your inbox

Published on medium