Weekend Scripter: Fun with Number Ranges and PowerShell


Summary: Use Windows PowerShell to split arrays of numbers into contiguous ranges.

Microsoft Scripting Guy, Ed Wilson, is here. Today I have a cool post from guest blogger, Zachary Loeber. Here is a little bit about Zachary:

Photo of Zachary Loeber

Zachary Loeber is a solution architect with PSC Group. Zachary is a seasoned network and systems architect and an administrator with hands-on experience for a wide range of technologies including Cisco/Juniper/HP networking, Exchange 2003/2007/2010/2013, Lync 2010/2013, Skype for Business, Active Directory, VMware, Linux, SAN technologies, and Windows PowerShell scripting.

Zachary holds several Microsoft and other industry certifications. He currently designs and deploys highly available Skype for Business and Exchange unified messaging solutions to replace or complement existing PBXes. Zachary contributes scripts regularly to the TechNet Gallery.

Zachary's contact info:

Blog: The Little Things
Twitter @zloeber
LinkedIn: Zachary Loeber

So without further ado (or even ADO.NET) here is Zachary…

Hey, Scripting Guy! Question How can I use Windows PowerShell to find the contiguous and non-overlapping ranges in a mixed array of numbers?

Hey, Scripting Guy! Answer Use advanced functions along with simple Windows PowerShell loops, logic, and objects.

Lately I’ve been doing a bunch of work with phone numbers in Skype for Business. As part of this work, I’ve been looking at lists of disparate numbers and found that there was no easy way to turn them into contiguous ranges.

Windows PowerShell is perfectly suited for such a task. Let's start with some example data. This is easy to create with Windows PowerShell:

# Gather 20 random numbers between 0 and 40

$test = @()

1..20 | Foreach {$test += (Get-Random -Minimum 0 -Maximum 40)}

To divide these ranges, I concocted a function that iterates through each number in the array, and returns objects with Begin and End property members for each number range found.

function Convert-ToNumberRange {

    [CmdletBinding()]

    param (

        [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, HelpMessage='Range of numbers in array.')]

        [int[]]$series

    )

    begin {

        $numberseries = @()

    }

    process {

        $numberseries += $series

    }

    end {

        $numberseries = @($numberseries | Sort | Select -Unique)

        $index = 1

        $initmode = $true

             # Start at the beginning

        $start = $numberseries[0]

            # If we only have a single number in the series, then go ahead and return it

        if ($numberseries.Count -eq 1) {

            return New-Object psobject -Property @{

                'Begin' = $numberseries[0]

                'End' = $numberseries[0]

            }

        }

        do {

            if ($initmode) {

                $initmode = $false

            }

            else {

                # If the current number minus the last number is not exactly 1, then the range has split

                # (so we have a non-contiguous series of numbers like 1,2,3,12,13,14....)

                if (($numberseries[$index] - $numberseries[$index - 1]) -ne 1) {

                    New-Object psobject -Property @{

                        'Begin' = $start

                        'End' = $numberseries[$index-1]

                    }

                    # Reset our starting point and begin again

                    $start = $numberseries[$index]

                    $initmode = $true

                }

            }

            $index++

        } until ($index -eq ($numberseries.length))

           # We should always end up with a result at the end for the last digits

        New-Object psobject -Property @{

            'Begin' = $start

            'End' = $numberseries[$index - 1]

        }

    }

}

Now we can take each of the numbers in our example arrays and very easily find each of the ranges within!

Image of command output

To show the contiguous number ranges in a nice table:

$test | Convert-ToNumberRange | ft -AutoSize

If you wanted a bit more familiar format, that is also easy to do:

Image of command output

To convert to a Windows PowerShell range operator format:

$test | Convert-ToNumberRange | Foreach { "$($_.Begin)..$($_.End)" }

How might this be helpful? I’ve personally used this technique to sanitize lists of provided phone numbers from an old PBX and from Active Directory. You can then use each number range as input to add your unassigned number ranges in Skype for Business. I also use this function to do more complex work with phone number ranges (such as finding overlapping number ranges).

~ Zachary

Thank-you, Zachary. You are right, this was fun.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Comments (2)

  1. tom says:

    I have a Question
    i’m try to customize a powershell script to update AD Info in bulk i have the Sample script

    Import-CSV .HR_Update.csv |

    ForEach-Object {Get-ADUser -Filter "Name -eq `"$($_.Name)`"" | Set-ADUser -EmailAddress $_.Email -OfficePhone $_.Phone}

    The fields that I need to update are

    DisplayName FirstName
    LastName Office
    Description EmailAddress
    E-mail mail

    all AD attributes/fields
    The reason for adding in all the Email field is because we have a (helpDesk service"Altiris" that need those field filled in AD in order to pull the user in to servicedesk that what i was told

  2. Hello Tom,

    All the fields you listed are available as parameters in Set-ADUser so you should be set with something like this:

    Import-CSV .HR_Update.csv | ForEach-Object {
    Get-ADUser -Filter "Name -eq `"$($_.Name)`"" |
    Set-ADUser -EmailAddress $_.Email `
    -OfficePhone $_.Phone `
    -DisplayName $_.Name `
    -GivenName $_.FirstName `
    -SurName $_.LastName `
    -Description $_.Description
    -Office $_.Office `
    -EmailAddress $_.EmailAddress
    }

Skip to main content