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.