Converting mobile numeric keypad numbers to its corresponding word Python

Question:

I need to create a function, where, If I give an input like 999933. It should give output as “ze”. It basically work as numeric mobile phone keypad. How can I this. I have searched to get some sample in internet. All, I got was quite opposite. Like, Giving the text as input and you will get the number. I couldn’t get the exact flow of, how to achieve that. Please let me know, how can i do that.

def number_to_text(val):

pass
Asked By: Jeyanth Kanagaraj

||

Answers:

Create a mapping pad_number to letter.
Use itertools.groupby to iterate over consecutive pad presses and calculate which letter we get.

import itertools

letters_by_pad_number = {"3": "def", "9": "wxyz"}

def number_to_text(val):
    message = ""
    # change val to string, so we can iterate over digits
    digits = str(val)
    # group consecutive numbers: itertools.groupby("2244") -> ('2', '22'), ('4','44')
    for digit, group in itertools.groupby(digits):
        # get the pad letters, i.e. "def" for "3" pad
        letters = letters_by_pad_number[digit]
        # get how many consecutive times it was pressed
        presses_number = len(list(group))
        # calculate the index of the letter cycling through if we pressed 
        # more that 3 times
        letter_index = (presses_number - 1) % len(letters)
        message += letters[letter_index]
    return message


print(number_to_text(999933))
# ze

And hardcore one-liner just for fun:

letters = {"3": "def", "9": "wxyz"}
def number_to_text(val):
    return "".join([letters[d][(len(list(g)) - 1) % len(letters[d])] for d, g in itertools.groupby(str(val))])

print(number_to_text(999933))
# ze
Answered By: RafalS

You need to

  • group the same digits together with the regex (d)1* that capture a digit then the same digit X times
  • use the value of a digit in the group to get the key
  • use the length of it to get the letter
phone_letters = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]

def number_to_text(val):
    groups = [match.group() for match in re.finditer(r'(d)1*', val)]
    result = ""
    for group in groups:
        keynumber = int(group[0])
        count = len(group)
        result += phone_letters[keynumber][count - 1]
    return result


print(number_to_text("999933")) # ze

Using list comprehension

def number_to_text(val):
    groups = [match.group() for match in re.finditer(r'(d)1*', val)]
    return "".join(phone_letters[int(group[0])][len(group) - 1] for group in groups)
Answered By: azro

The other answers are correct, but I tried to write a less brief more real world (including doctests) explanation of how the previous results worked:

dialpad_text.py:

# Import the groupby function from itertools, 
# this takes any sequence and returns an array of groups by some key
from itertools import groupby

# Use a dictionary as a lookup table
dailpad = {
  '2': ['a', 'b', 'c'],
  '3': ['d', 'e', 'f'],
  '4': ['g', 'h', 'i'],
  '5': ['j', 'k', 'l'],
  '6': ['m', 'n', 'o'],
  '7': ['p', 'q', 'r', 's'],
  '8': ['t', 'u', 'v'],
  '9': ['w', 'x', 'y', 'z'],
}

def dialpad_text(numbers):
  """
  Takes in either a number or a string of numbers and creates
  a string of characters just like a nokia without T9 support

  Default usage:
  >>> dialpad_text(2555222)
  'alc'

  Handle string inputs:
  >>> dialpad_text('2555222')
  'alc'

  Handle wrapped groups:
  >>> dialpad_text(2222555222)
  'alc'

  Throw an error if an invalid input is given
  >>> dialpad_text('1BROKEN')
  Traceback (most recent call last):
    ...
  ValueError: Unrecognized input "1"
  """

  # Convert to string if given a number
  if type(numbers) == int:
    numbers = str(numbers)

  # Create our string output for the dialed numbers
  output = ''

  # Group each set of numbers in the order 
  # they appear and iterate over the groups.
  # (eg. 222556 will result in [(2, [2, 2, 2]), (5, [5, 5]), (6, [6])])
  # We can use the second element of each tuple to find 
  # our index into the dictionary at the given number!
  for number, letters in groupby(numbers):
    # Convert the groupby group generator into a list and
    # get the offset into our array at the specified key
    offset = len(list(letters)) - 1

    # Check if the number is a valid dialpad key (eg. 1 for example isn't)
    if number in dailpad.keys():
      # Add the character to our output string and wrap
      # if the number is greater than the length of the character list
      output += dailpad[number][offset % len(dailpad[number])]
    else:
      raise ValueError(f'Unrecognized input "{number}"')

  return output

Hope this helps you understand what’s going on a lower level! Also if you don’t trust my code, just save that to a file and run python -m doctest dialpad_text.py and it will pass the doctests from the module.

(Notes: without the -v flag it won’t output anything, silence is golden!)

Answered By: SeedyROM

A slightly Modified answer of RaFalS without using itertools

import itertools
from collections import defaultdict

letters_by_pad_number = {"3": "def", "9": "wxyz"}

val = 999933

message = ""

digits = str(val)
num_group = defaultdict(int)

for digit in digits:
    num_group[digit] += 1

for num in num_group.keys():
    message += letters_by_pad_number[num][num_group[num]-1]

print(message)
# ze
Answered By: Manohar allu
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.