Detecting digit combinations in a number with PowerShell

How we can inspect a number like 143256789 and determine it contains the digits 1-9

James Montgomery

5 minute read

TL; DR

How do you pass the time on a car journey? Most of us have memories of “I spy” or other game.

Lately, I’ve been looking out for number combinations after a car I owned passed 123k miles - specifically 123456 miles. What’s more, the trip computer almost made it read 1234567890!

As time passed, I was looking out for other odometer readings. 123465 miles was the next number of interest. The trip computer, however, didn’t complete the set.

I began thinking, could I determine future combinations that would satisfy the 0-9 game from a given starting position? Below I discuss my thoughts along the way.

You can find the resulting script in my GitHub repository.

Starting with the odometer

A core capability to completing the task would be to determine which digits are present in a number. This motivation was the driving force for my previous post.

Beginning with the odometer 1-6 game, are the numbers 1-6 present?

For example:

123456 (yes)
123457 (no)
123458 (no)
… (noes)
123465 (yes)
… (noes)
123546 (yes)

In my previous post, I reviewed regex as an option via -match to determine if a range of digits was present in a number. I omitted a more complex example which I illustrate here. Inspired by this Stackoverflow post.

$theseUniqueDigits = 654211
$match1 = "\b(?:([123456])(?![123456789]*\1))+\b"
$theseUniqueDigits -match $match1
#Expecting False output
$theseUniqueDigits = 654231
$theseUniqueDigits -match $match1
#Expecting True output

As I sought to expand my example to process any number, I noticed my regex pattern change based on number length. For example:

1-6
\b(?:([123456])(?![123456789]*\1))+\b
1-9
\b(?:([123456789])(?![123456789]*\1))+\b

Regex101 interactive example (1-6 range) and (1-9 range)

I generated the required regex by evaluating the candidate number’s length:

$lookingForInts = 1.. $thisLength
$matchFor = $lookingForInts -join ""
$match1 = "\b(?:([$matchFor])(?![123456789]*\1))+\b"

Generating regex requires some care. The variable in the regex generation appears trivial enough that verification seems superfluous. I elected to validate it anyway with this approach:

$confirmRegex = "\\b\(\?\:\(\[(1|12|123|1234|12345|123456|1234567|12345678|123456789)\]\)\(\?\!\[123456789\]\*\\1\)\)\+\\b"
if($evaluateRegex){
        write-debug "Confirming regex: $($match1 -match $confirmRegex)"
    }

I wrote a validation function using another approach in case there was any doubt in number processing:

Function testNumber
{
    param([int64]$lookingIn, $lookingFor)
    Write-Debug "Processing $lookingIn for digits $lookingFor"
    $foundInts = $true
    #Initialise found to true and set to false if we cannot locate a digit

    $lookingFor | ForEach-Object {
        if ($lookingIn.tostring().Contains([string]$_))
        {
            Write-Debug "[string contains]$_"
        }
        else {
            Write-Debug "[string does not contain]$_"
            $foundInts = $false
        }
    }

    return $foundInts
}

Including the trip computer

To keep things simple, I have assumed the trip computer value is a three-digit number - omitting the tenths digit ordinarily present.

Therefore, we could have the following examples:

Odometer = 545, Trip= 122
Odometer = 546, Trip= 123
Odometer = 547, Trip= 124

The second example has a complete set of 1-6 when combining the digits of the two numbers.

We achieve this by multiplying the odometer reading by one thousand and adding the trip value.

For example:

Odometer = 545, Trip= 122, Combined=545000+122=545122
Odometer = 546, Trip= 123, Combined=546000+123=546123
Odometer = 547, Trip= 124. Combined=547000+124=547125

We can treat the combined number with the same logic as the odometer approach. Which seemed fine until I explored number sets at the higher end of the scale.

If you recall, the car had passed 123k miles. Therefore the combined digits of the odometer(6) and trip computer(3) produce a number length of 9.

I added another zero onto the odometer range 1-1.1 million miles. This mileage was somewhat unrealistic in terms of the car’s range capability but a compelling edge case.

It was at this point that my regex approach broke down. I tried extending it to consider multiples of digits, but I didn’t get very far.

What does a complete number set look like at ten digits long?

For the sake of putting a line in the sand, and producing an edge case, I elected to omit zero as a digit of interest. The first numbers of length ten evaluate as follows:

1234567890 (no)
1234567891 (yes)
1234567892 (yes)
… (yeses)
1234567899 (yes)
1234567900 (no)

I had some inspiration from this Reddit thread, which gave me feedback on the previous digit detection post. What if instead of changing the validation approach, I could clean the data by reviewing unique digits?

The first numbers of length ten evaluate as follows in this approach:

1234567890 –> 1234567890 (no)
1234567891 –> 123456789 (yes)
1234567892 –> 123456789 (yes)
… (yeses)
1234567899 –> 123456789 (yes)
1234567900 –> 123456790 (no)

We achieve this as follows:

  • Convert the combined number into a String object. We’ll call this our source value.
  • Convert the string into an array of characters.
  • We pass the result into select-object, which is configured to accept unique objects.
  • Lastly, we join the characters back into a String object with -join. We’ll call this our result value.

By examining the length of the result, as compared to the source, we can reach conclusions that save us from digit detection in every case.

If the length of the result matches the source, all digits are unique. We evaluate.

For example:

12357 (5 digits) –> 12357 (5 digits, evaluate for the digits 1-5)
54321 (5 digits) –> 54321 (5 digits, evaluate for the digits 1-5)

If the length of the result is less than the length of the source, we have some repetition. If the number of source digits is less than 9, we do not evaluate.

For example:

123357 (5 digits) –> 12357 (4 digits, do not evaluate)

However, where the number of source digits is nine or more, we evaluate.

For example:

123456708 (9 digits) –> 123456708 (9 digits, evaluate this number for digits 1-9)
123987456 (9 digits) –> 123987456 (9 digits, evaluate this number for digits 1-9)
1234567900 (10 digits) –> 123456790 (9 digits, evaluate this number for the digits 1-9)

Conclusion

It feels good to solve a purely academic problem at times. I’ve limited solving this involving only two data inputs, and a randomly selected initial trip value.

The trip computer value rolls over at 999 in my model, but what if we reset it to 0 deliberately. When would we do that to maximise our collection of number combinations in the future?

What other data could we consider to complete this number game? The clock, the temperature, GPS coordinates, our current programmed route?

Acknowledgements