# Validate 10 digits on a few conditions

I want to write a function that validates the German tax identification number (Steuer-ID).
The identification number consists of eleven digits. The last digit is only for checking. This is not what this question is about.

The first ten digits of the identification number contain one digit exactly twice and another digit not at all (as of 2016, a triple occurring digit is also possible, correspondingly then two digits not at all; in case of three equal digits, only two may be in immediate succession, but not all three), the other eight (as of 2016: also seven) digits each exactly once. The first digit must not be 0.

My thought process:

• With a length of 10 digits, one digit must occur twice anyway. But it may occur three times. So I check if the groupList.Count property is 8 or 9. Probably the Boolean "double_or_triple_occurrence" is unnecessary then...
• But there must not be a double occurrence of another digit.
• there must not be 3 of the same digits directly behind each other, only 2 of them.

For Germans among the readers, this is the Wikipedia site.

This is how I implemented it:

Public NotInheritable Class FormMain
Label1.Text = ""
End Sub

Private Sub ButtonStart_Click(sender As Object, e As EventArgs) Handles ButtonStart.Click
Validate_SteuerId(TextBox1.Text)
End Sub

Private Sub Validate_SteuerId(StringToCheck As String)
Label1.Text = ""
If String.IsNullOrEmpty(StringToCheck) Then
Label1.Text = "Leer" ' Empty
Return
End If

If StringToCheck.StartsWith("0") Then
Label1.Text = "Die erste Ziffer darf nicht 0 sein." ' The first digit must not be zero.
Return
End If

If StringToCheck.Length < 11 Then
Label1.Text = "Zu kurz" ' too short
Return
ElseIf StringToCheck.Length > 11 Then
Label1.Text = "Zu lang" ' too long
Return
End If

Dim myInts As New List(Of Integer)
Dim charArray As Char() = StringToCheck.ToArray()
For i As Integer = 0 To charArray.Length - 2 Step 1
Dim rslt As Integer
If Integer.TryParse(charArray(i), rslt) Then
Else
Label1.Text = "Fehler beim Parsen" ' error
Return
End If
Next
If Not Check_occurrence_of_each_digit(myInts) Then
Label1.Text = "Fehler"
End If
End Sub

Private Function Check_occurrence_of_each_digit(Ints As List(Of Integer)) As Boolean
Dim groupList As List(Of IGrouping(Of Integer, Integer)) = Ints.GroupBy(Function(x) x).Where(Function(y) y.Count <= 3).ToList()
If groupList.Count <> 8 AndAlso groupList.Count <> 9 Then
Return False
End If

Dim double_or_triple_occurrence As Boolean = False

For i As Integer = 0 To groupList.Count - 1 Step 1
Dim currentNumber As Integer = groupList(i).Key
Dim howOften As Integer = groupList(i).Count
If howOften = 2 OrElse howOften = 3 Then
double_or_triple_occurrence = True
End If
Debug.WriteLine($"Die Zahl {currentNumber} kommt {howOften} Mal vor.") Next If double_or_triple_occurrence Then Dim bools As New List(Of Boolean) For i As Integer = 0 To Ints.Count - 1 Step 1 Dim u As Integer = i bools.Add(Ints.Any(Function(x) x = u)) Next If bools.Where(Function(x) x = False).Count > 1 Then Return False End If End If 'max 2 same digits one after the other Dim old As Integer Dim middle As Integer Dim farest As Integer For i As Integer = 0 To Ints.Count - 3 Step 1 old = Ints(i) middle = Ints(i + 1) farest = Ints(i + 2) If old = Ints(i + 1) AndAlso middle = Ints(i + 1) AndAlso farest = Ints(i + 1) Then Return False End If Next Return True End Function End Class  I would like to ask you to shorten the amount of code lines. I appreciate. ## 1 Answer There is no separation between interface and logic. That also makes it difficult to reuse that function in another context, for instance in a different form. Suggestion: convert your routine into a stand-alone function. All it has to do is to return a result, that can be used by the caller. It should not bother with the UI at all. Then I assume we will want the function to return two values: one boolean value that tell us whether the check is successful, and another optional value (string) containing a description of the error, if any. Afaik a VB.net function can return only one value unlike Python for example. To get round this problem you could use a structure or another composite data type. Or you could simply pass additional arguments to your function as ByRef and retrieve their resulting values, after they have been changed by the function. My proposed implementation is to return a boolean value, and return the error message as a ByRef argument like this: Private Function Validate_SteuerId(ByVal StringToCheck As String, Optional ByRef ValidationResults As String = "") As Boolean If String.IsNullOrEmpty(StringToCheck) Then ValidationResults = "Leer" ' Empty Return False End If ' more stuff goes here Return True ' default outcome, make sure all code paths return a value End Function  This is pretty much like you did for Check_occurrence_of_each_digit, we just added an optional return value. Now your function is completely independent from the form. It's easy refactoring. And you can call the function like this when clicking on the button: Private Sub btnCheck_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCheck.Click Dim reason As String = "" If Check_SteuerId(Me.TextBox1.Text, reason) Then Label1.Text = "Validation OK" Else Label1.Text = "Validation failed - reason: " & reason End If End Sub  Alternatives: check if your version of VB.net provides the Tuple or KeyValuePair types. These two blocks could be merged into one: If StringToCheck.Length < 11 Then Label1.Text = "Zu kurz" ' too short Return ElseIf StringToCheck.Length > 11 Then Label1.Text = "Zu lang" ' too long Return End If  Just check if the length is 11, otherwise return an error message saying that 11 characters are expected, that should be enough for the developer. He/she can easily figure out if the input value is too short or too long. But you can simplify logic a little bit: instead of checking that the length is 11 digits, that the string does not begin with zero etc, you could use a regular expression. Add this import at the top of your code: Imports System.Text.RegularExpressions  Then put the block inside your function: Dim re As New Regex("^[1-9][0-9]{10}$")
If Not re.Match(StringToCheck).Success Then
ValidationResults = "Expected: a string of 11 digits that does not begin with zero"
Return False
End If


Using regular expressions and backreferences, it is also possible to verify certain patterns, for example if you wanted to check that the string contains at least one digit repeated exactly 3 times in a row you could do this:

Dim matches As MatchCollection = Regex.Matches(TextBox1.Text, "(.)\1{2}")
Console.WriteLine("matches: " & matches.Count)
' Loop over matches.
For Each m As Match In matches
' Loop over captures.
For Each c As Capture In m.Captures
' Display.
Console.WriteLine("Index={0}, Value={1}", c.Index, c.Value)
Next
Next


Thus, 12220333456 will return two matches: 222 and 333 respectively. Accordingly matches.Count will be equal to 2.

Now if you want to make sure that no digit is repeated more than twice in a row, you could write Regex.Matches(TextBox1.Text, "(\d)\1{2,}") and verify that matches.Count = 0. Notice the trailing comma after the 2 - it means two or more repetitions of the previously matched digit. 333 will match but 22 won't.

The dot in a regex means any character. \d represents a single digit. Here we are being more precise and match on digits only.

Regular expressions are very powerful but can get very complex too. For some "simple" checks they are convenient shorthand methods and could replace some of your parsing routines.

• Hi Kate, thanks for your answer and for the solutions using regex and passing the string by reference. To your point about separating GUI and logic: I have indeed created a new project for you. The logic is now built into the real program. Accepted. 😃 Nov 19, 2022 at 9:15