64

I want to take the nth digit from an N digit number in python. For example:

number = 9876543210
i = 4
number[i] # should return 6

How can I do something like that in python? Should I change it to string first and then change it to int for the calculation?

Chris Mueller
  • 6,490
  • 5
  • 29
  • 35
zakaria musa
  • 689
  • 1
  • 5
  • 6
  • 1
    `int(str(number)[i-1])`. Or if you need to handle all digits: `for index, digit in enumerate(str(number), start=1): digit = int(digit)` – Steven Rumbalski Sep 22 '16 at 16:50
  • 3
    Stack Overflow is not a code-writing or tutorial service. Please [edit] your question and post what you have tried so far, including example input, expected output, the actual output (if any), and the **full text** of any errors or tracebacks. – MattDMo Sep 22 '16 at 17:03

8 Answers8

101

You can do it with integer division and remainder methods

def get_digit(number, n):
    return number // 10**n % 10

get_digit(987654321, 0)
# 1

get_digit(987654321, 5)
# 6

The // performs integer division by a power of ten to move the digit to the ones position, then the % gets the remainder after division by 10. Note that the numbering in this scheme uses zero-indexing and starts from the right side of the number.

Chris Mueller
  • 6,490
  • 5
  • 29
  • 35
  • 3
    The OP is using 1-based indexing starting at the left side of the number. – Steven Rumbalski Sep 22 '16 at 16:54
  • 3
    @Steven You are right that this indexes from the right side of the number instead of the left. It could be manipulated to work the other way, but this seems more in line with how you would think about the positions in a number. I.E. index 0 gets the 1s position, index 1 gets the 10s position, and so on. – Chris Mueller Sep 22 '16 at 17:00
  • 2
    In addition to Steven's comment, this solution also fails for numbers between 0 and 1. – indigochild Jan 04 '21 at 00:29
  • 2
    @indigochild It certainly doesn't 'fail' for numbers between 0 and 1 in either the `number` or the `n` position. What behavior did you expect? – Chris Mueller Jan 04 '21 at 13:52
55

First treat the number like a string

number = 9876543210
number = str(number)

Then to get the first digit:

number[0]

The fourth digit:

number[3]

EDIT:

This will return the digit as a character, not as a number. To convert it back use:

int(number[0])
Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
  • 1
    This solution works but it is not optimized because it uses more space. see this: https://stackoverflow.com/a/39644726/375966 – Afshin Mehrabani Jun 12 '17 at 15:11
  • 4
    Definitely would use more elegant and efficient solution from Chris – 5hrp Aug 25 '17 at 07:08
  • What about having a float? Like 3.125e-10 – Red Sparrow Sep 11 '18 at 11:13
  • It is slow and inefficient. Simply use `number // 10**n % 10` as this answer suggests: https://stackoverflow.com/a/39644726/7339624 – Peyman Mar 30 '20 at 08:31
  • This solution solves many more "digit of a number" problems than the one using integer division and modulus. Try to find the leading (left-most) digit of the following numbers using both: 723981 4,212,633 1_000_000 983.5 2.04e7 – grjash Dec 24 '20 at 23:03
  • premature optimization is the root of all evil – cryanbhu Dec 29 '22 at 09:05
13

I was curious about the relative speed of the two popular approaches - casting to string and using modular arithmetic - so I profiled them and was surprised to see how close they were in terms of performance.

(My use-case was slightly different, I wanted to get all digits in the number.)

The string approach gave:

         10000002 function calls in 1.113 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 10000000    1.113    0.000    1.113    0.000 sandbox.py:1(get_digits_str)
        1    0.000    0.000    0.000    0.000 cProfile.py:133(__exit__)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

While the modular arithmetic approach gave:


         10000002 function calls in 1.102 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 10000000    1.102    0.000    1.102    0.000 sandbox.py:6(get_digits_mod)
        1    0.000    0.000    0.000    0.000 cProfile.py:133(__exit__)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

There were 10^7 tests run with a max number size less than 10^28.

Code used for reference:

def get_digits_str(num):
    for n_str in str(num):
        yield int(n_str)


def get_digits_mod(num, radix=10):

    remaining = num
    yield remaining % radix

    while remaining := remaining // radix:
        yield remaining % radix


if __name__ == '__main__':

    import cProfile
    import random

    random_inputs = [random.randrange(0, 10**28) for _ in range(10**7)]

    with cProfile.Profile() as str_profiler:
        for rand_num in random_inputs:
            get_digits_str(rand_num)

    str_profiler.print_stats(sort='cumtime')

    with cProfile.Profile() as mod_profiler:
        for rand_num in random_inputs:
            get_digits_mod(rand_num)

    mod_profiler.print_stats(sort='cumtime')
shayaan
  • 1,482
  • 1
  • 15
  • 32
5

I would recommend adding a boolean check for the magnitude of the number. I'm converting a high milliseconds value to datetime. I have numbers from 2 to 200,000,200 so 0 is a valid output. The function as @Chris Mueller has it will return 0 even if number is smaller than 10**n.

def get_digit(number, n):
    return number // 10**n % 10

get_digit(4231, 5)
# 0

def get_digit(number, n):
    if number - 10**n < 0:
        return False
    return number // 10**n % 10

get_digit(4321, 5)
# False

You do have to be careful when checking the boolean state of this return value. To allow 0 as a valid return value, you cannot just use if get_digit:. You have to use if get_digit is False: to keep 0 from behaving as a false value.

Nathan Pyle
  • 481
  • 6
  • 9
  • 1
    a function that can return 0 (int) as a valid answer while returning False (bool) for issues is a recipe for bugs. Better use assert or raise an exception, if you really need to check that. Also, 0 if the number is less than 10**n is not wrong mathematically, so I don’t see why it should be treated as such. – spider Jun 09 '19 at 16:36
3

I'm very sorry for necro-threading but I wanted to provide a solution without converting the integer to a string. Also I wanted to work with more computer-like thinking so that's why the answer from Chris Mueller wasn't good enough for me.

So without further ado,

import math

def count_number(number):
    counter = 0
    counter_number = number
    while counter_number > 0:
        counter_number //= 10
        counter += 1
    return counter


def digit_selector(number, selected_digit, total):
    total_counter = total
    calculated_select = total_counter - selected_digit
    number_selected = int(number / math.pow(10, calculated_select))
    while number_selected > 10:
        number_selected -= 10
    return number_selected


def main():
    x = 1548731588
    total_digits = count_number(x)
    digit_2 = digit_selector(x, 2, total_digits)
    return print(digit_2)


if __name__ == '__main__':
    main()

which will print:

5

Hopefully someone else might need this specific kind of code. Would love to have feedback on this aswell!

This should find any digit in a integer.

Flaws:

Works pretty ok but if you use this for long numbers then it'll take more and more time. I think that it would be possible to see if there are multiple thousands etc and then substract those from number_selected but that's maybe for another time ;)

Usage:

You need every line from 1-21. Then you can call first count_number to make it count your integer.

x = 1548731588
total_digits = count_number(x)

Then read/use the digit_selector function as follows:

digit_selector('insert your integer here', 'which digit do you want to have? (starting from the most left digit as 1)', 'How many digits are there in total?')

If we have 1234567890, and we need 4 selected, that is the 4th digit counting from left so we type '4'.

We know how many digits there are due to using total_digits. So that's pretty easy.

Hope that explains everything!

Han

PS: Special thanks for CodeVsColor for providing the count_number function. I used this link: https://www.codevscolor.com/count-number-digits-number-python to help me make the digit_selector work.

HanW
  • 31
  • 1
2

Ok, first of all, use the str() function in python to turn 'number' into a string

number = 9876543210 #declaring and assigning
number = str(number) #converting

Then get the index, 0 = 1, 4 = 3 in index notation, use int() to turn it back into a number

print(int(number[3])) #printing the int format of the string "number"'s index of 3 or '6'

if you like it in the short form

print(int(str(9876543210)[3])) #condensed code lol, also no more variable 'number'
GeekOverdose
  • 325
  • 2
  • 10
1

Arithmetical approach to access the digits of a integer number.

Fixed a limitation of Chris Mueller's solution: if a digit outside the number domain is accessed then the result is always 0:

get_digit(12345, 10) # Chris Mueller
# 0

All that is needed to fix it is to track the position of the leading digit.

The original question refers to the big-endian sorting type but also a little-endian implementation is included.

def int_to_list(n:int) -> list[int]:
    if not n:
        return [0]
    
    n_digits = 0
    while n >= 10**n_digits:
        n_digits += 1

    return [n // 10**i % 10 for i in range(n_digits)] #            big-endian
    #return [n // 10**i % 10 for i in range(n_digits-1, -1, -1)] # little-endian


x = 9876543210

print(int_to_list(x)) # big-endian ordering
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(int_to_list(x)[4])
#4
print(int_to_list(x)[20])
IndexError: list index out of range
cards
  • 3,936
  • 1
  • 7
  • 25
-1

Here's my take on this problem.

I have defined a function 'index' which takes the number and the input index and outputs the digit at the desired index.

The enumerate method operates on the strings, therefore the number is first converted to a string. Since the indexing in Python starts from zero, but the desired functionality requires it to start with 1, therefore a 1 is placed in the enumerate function to indicate the start of the counter.

def index(number, i):

    for p,num in enumerate(str(number),1):

        if p == i:
            print(num)
AMK
  • 1
  • 1