27

Say I have

var i = 987654321;

Is there an easy way to get an array of the digits, the equivalent of

var is = new int[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

without .ToString()ing and iterating over the chars with int.Parse(x)?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrew Bullock
  • 36,616
  • 34
  • 155
  • 231

11 Answers11

39
public Stack<int> NumbersIn(int value)
{
    if (value == 0) return new Stack<int>();

    var numbers = NumbersIn(value / 10);

    numbers.Push(value % 10);

    return numbers;
}

var numbers = NumbersIn(987654321).ToArray();

Alternative without recursion:

public int[] NumbersIn(int value)
{
    var numbers = new Stack<int>();

    for(; value > 0; value /= 10)
        numbers.Push(value % 10);

    return numbers.ToArray();
}
Garry Shutler
  • 32,260
  • 12
  • 84
  • 119
24

I know there are probably better answers than this, but here is another version:

You can use yield return to return the digits in ascending order (according to weight, or whatever it is called).

public static IEnumerable<int> Digits(this int number)
{
    do
    {
        yield return number % 10;
        number /= 10;
    } while (number > 0);
}

12345 => 5, 4, 3, 2, 1

Tot Zam
  • 8,406
  • 10
  • 51
  • 76
Svish
  • 152,914
  • 173
  • 462
  • 620
10

Just did a benchmark on different methods and here is the results:

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041
Intel Core i7-8705G CPU 3.10GHz (Kaby Lake G), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.301
  [Host]     : .NET Core 3.1.5 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.27001), X64 RyuJIT
  DefaultJob : .NET Core 3.1.5 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.27001), X64 RyuJIT


                         Method |      Mean |    Error |   StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
------------------------------- |----------:|---------:|---------:|-------:|------:|------:|----------:|
                          Stack |  89.06 ns | 2.130 ns | 6.179 ns | 0.0592 |     - |     - |     248 B |
                    SharedArray |  84.64 ns | 1.765 ns | 3.685 ns | 0.0153 |     - |     - |      64 B |
 PreallocateUsingNumberOfDigits |  39.15 ns | 0.861 ns | 2.499 ns | 0.0153 |     - |     - |      64 B |
                    IEnumerable | 246.53 ns | 4.926 ns | 9.372 ns | 0.0610 |     - |     - |     256 B |

In order mean speed:

PreallocateUsingNumberOfDigits

Mean: ~39.15ns Error: 0.861ns Alloc: 64 B

public static int[] GetDigits(int n)
{
    if (n == 0)
        return new[] {0};
    
    var x = Math.Abs(n);

    var numDigits = NumberOfDigits(x);

    var res = new int[numDigits];
    var count = 0;

    while (x > 0)
    {
        res[count++] = x % 10;
        
        x /= 10;
    }
    
    Array.Reverse(res);

    return res;
}

public static int NumberOfDigits(int n)
{
    if (n >= 0)
    {
        if (n < 10) return 1;
        if (n < 100) return 2;
        if (n < 1000) return 3;
        if (n < 10000) return 4;
        if (n < 100000) return 5;
        if (n < 1000000) return 6;
        if (n < 10000000) return 7;
        if (n < 100000000) return 8;
        if (n < 1000000000) return 9;
        return 10;
    }
    else
    {
        if (n > -10) return 2;
        if (n > -100) return 3;
        if (n > -1000) return 4;
        if (n > -10000) return 5;
        if (n > -100000) return 6;
        if (n > -1000000) return 7;
        if (n > -10000000) return 8;
        if (n > -100000000) return 9;
        if (n > -1000000000) return 10;
        return 11;
    }
}

SharedArray

Mean: ~84.64ns Error: 1.765ns Alloc: 64 B

public static int[] GetDigits_SharedPool(int n)
{
    if (n == 0)
        return new[] {0};
    
    var x = Math.Abs(n);
    
    var pool = ArrayPool<int>.Shared.Rent(11);
    var count = 0;

    while (x > 0)
    {
        pool[count++] = x % 10;
        
        x /= 10;
    }

    var res = new int[count];
    
    Array.Copy(pool, res, count);
    
    Array.Reverse(res);
    
    ArrayPool<int>.Shared.Return(pool);

    return res;
}

Stack

Mean: ~89.06ns Error: 2.130ns Alloc: 248 B

From: @Peter Lillevold answer

public int[] Stack()
{
    var list = new Stack<int>(32);
    var remainder = digit;
    do
    {
        list.Push(remainder % 10);
        remainder /= 10;
    } while (remainder != 0);

    return list.ToArray();
}

IEnumerable

Mean: ~246.53ns Error: 4.926ns Alloc: 256 B

From @Svish's Answer

public static IEnumerable<int> Digits_IEnumerable(int number)
{
    do
    {
        yield return number % 10;
        number /= 10;
    } while (number > 0);
}

Code

See here: michal-ciechan/GetDigitsBenchmark

Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118
8

Another alternative which don't uses recursion and uses a Stack that avoids reallocation on every insert (at least for the first 32 digits):

var list = new Stack<int>(32);
var remainder = 123456;
do
{
    list.Push(remainder % 10);
    remainder /= 10;
} while (remainder != 0);

return list.ToArray();

And yes, this method also works for 0 and negative numbers.

Interestingly, give this algorithm a negative number -123456 and you will get {-1, -2, -3, -4, -5, -6}

Update: switched from using List to Stack since this automatically gives the correct order.

Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
5
var x = new Stack<int>();
do
{
    x.Push(i % 10);
    i /= 10;
} while (i > 0);
return x.ToArray();
marklam
  • 5,346
  • 1
  • 24
  • 26
2

In short: use loop which divide number modulo 10 (%) to get reminder (each digit) and put it into array.

Grzegorz Gierlik
  • 11,112
  • 4
  • 47
  • 55
1

Strings and can fun (some of the other options would be faster... but this is pretty easy)

var @is = 987654321.ToString().Select(c => c - 48).ToArray();
Matthew Whited
  • 22,160
  • 4
  • 52
  • 69
1

.NET 4.7.1 or above:

IEnumerable<long> GetDigits(long value) =>
  value == 0 ? new long[0] : GetDigits(value / 10).Append(value % 10)

.NET 3.5 - 4.7:

IEnumerable<long> GetDigits(long value) =>
  value == 0 ? new long[0] : GetDigits(value / 10).Concat(new[] { value % 10 });
Alex Butenko
  • 3,664
  • 3
  • 35
  • 54
0

This converts the int value to string, then to an array of chars and finally to an int array.

var myInt = 31337;
var myIntArray = Array.ConvertAll(myInt.ToString().ToCharArray(), x => (int)Char.GetNumericValue(x));

I used .ToCharArray() because it should be faster than .ToArray().

volkit
  • 1,173
  • 14
  • 21
0

For the LINQ lovers as ONE LINER =>

var number = 8892366;
//Edit 17.11.2021

var splittedList = number.ToString().Select(x => (int)char.GetNumericValue(x)).ToArray();

// Output : int[] {8, 8, 9, 2, 3, 6, 6} 
  • A `string` is already an `array`; the `ToCharArray()` is redundant. And you should be using the built-in method of getting the numeric value from a char: `( int )char.GetNumericValue( x )`. Finally, return an array, not a list. – Metro Smurf Nov 15 '21 at 00:33
  • U re right. To answer the question as an array. In my case i ve need a list. – Ivan Rakitin Nov 17 '21 at 07:05
-2

This does convert to string and iterate over the characters, but it does it sort of automatically and in a one-liner:

var i = 987654321;
var @is = i.ToString().Select(c => c - '0').ToArray();
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794