Home > Software design >  Return how many unique digitis a number contains
Return how many unique digitis a number contains

Time:01-24

I have a string of digits and I want to count how many unique digits the string contains.

Example:

111222
1002345
000000

Expected output:

111222 2
1002345 6
000000 1

I have achieved this using the following code:

private static int Counter(string ID)
{
    char[] numbers = new char[]{'0','1','2','3','4','5','6','7','8','9'};
    List<int> listofmatched = new List<int>();
    var split = ID.ToArray();
    foreach (var num in split)
    {
        if (numbers.Contains(num))
        {
            if (listofmatched.Contains(num))
            {
                continue;
            }
            else
            {
                listofmatched.Add(num);
            }
        }
    }
    return listofmatched.Count;
}

Is there any way to improve the code above? I feel like there's unnecessary loops.

CodePudding user response:

Don't know if it fits your defintion of "improvement", but you could do it like this:

str.Where(x => char.IsDigit(x)).GroupBy(x => x).Count();

See it here:

https://dotnetfiddle.net/t5OW6T

Edit:

As noticed in the comments, you could also use Distinct isntead of GroupBy(x => x).

GroupBy(x => x) can be useful if you want to know which digit occurs how often in the string, this can be done like this:

str.Where(x => char.IsDigit(x)).GroupBy(x => x).Select(x => (x.Key, x.Count()))

Where instead of calling Count on the "whole" grouping result, you evaluate the Count for each individual group of the grouping result.

You may find an example here:

https://dotnetfiddle.net/D60ygZ

CodePudding user response:

You can add the digits to HashSet, and then return its size

static int Counter(string ID)
{
    var hs = new HashSet<char>();
    foreach (var c in ID) 
        if (char.IsDigit(c)) 
            hs.Add(c);
    return hs.Count;
}

CodePudding user response:

An alternative that is working on ReadOnlySapn<char> for those who prefer that:

public static int CountWithHashSetOnReadOnlySpan(ReadOnlySpan<char> input)
{
    HashSet<char> hs = new();
    foreach (var c in input) if (char.IsDigit(c)) hs.Add(c);
    return hs.Count;
}

(After his edit, this is now pretty much identical to Nehorai's answer, just that this works on ReadOnlySpan<char> instead of string. Usage is the same. I also updated the Benchmark, which shows that performance is also pretty much identical.)

I ran this against the accepted code and its Distinct variation:

|                Method |       Mean |    Error |   StdDev |   Gen0 | Allocated |
|---------------------- |-----------:|---------:|---------:|-------:|----------:|
|       GroupByOnString | 1,257.5 ns | 24.83 ns | 23.23 ns | 0.2899 |    2432 B |
|      DistinctOnString |   984.7 ns | 13.76 ns | 12.88 ns | 0.1926 |    1616 B |
|       HashSetOnString |   313.3 ns |  6.25 ns |  6.14 ns | 0.1202 |    1008 B |
| HashSetOnReadOnlySpan |   313.3 ns |  3.20 ns |  2.99 ns | 0.1202 |    1008 B |

Mind that this is nano seconds. So it's actually not that much difference.

The comment

IsDigit also considers Unicode digits, i.e. digits of other writing systems. If this isn’t wanted we can use IsAsciiDigit since .NET 7.

applies here, too.

Here is a Fiddle.

  • Related