Determine the third octet of an IP address based on user entered site number

I am trying to create a function for a larger script to determine the third octet of a site IP address based off a user entered site number. I have actually come up with the function, but was hoping that someone could help me come up with something that is simpler and more elegant, instead of the multitude of nested if statements that I came up with. Here are the rules:

• For sites numbered 0001 - 0255, the third octet is the last 3 digits of the site number, less any zeroes preceding a real number (e.g. Site 0007 = 10.10.7.xxx)
• For sites numbered 0256 - 0399 and 0500 - 1999 (except 1202), the third octet is the last 2 digits of the site number less any zeroes preceding a real number (e.g. site 0315 = 10.22.15.xxx)
• For sites numbered 401 - 499, the third octet is the last two digits of the site number +100 (e.g. 0401 = 10.20.101.xxx)
• Site 1202 is an exception to the above rule, where the third octet is the last 3 digits (.202)
• For sites 5000 and above (5000 - 9999), the first digit is dropped, and then the rules above are followed. (e.g. 8248 = 10.12.48.xxx)

Here is what I have come up with. It is not very elegant, but it seems to work. Note that the first three lines will be in another function in the script, but are here for testing purposes.

$global:site = Read-Host 'What is the 4 digit site number?' #must include leading zeros [int]$global:sitecheck=$site #need for comparasion operators [string]$global:sitestring=$site if ($sitecheck -le 255)
{
if ($sitestring.substring(0,3) -eq '000') #(i.e. 0001 - 0009) {$global:thirdoct = $sitestring.substring(3,1) #(i.e. 1-9) } elseif ($sitestring.substring(0,2) -eq '00') #(i.e. 0010 - 0099)
{
$global:thirdoct =$sitestring.substring(2,2) #(i.e. 10 - 99)
}
elseif ($sitestring.substring(0,1) -eq '0') #(i.e. 0100 - 0255) {$global:thirdoct = $sitestring.substring(1,3) #(i.e. 100 - 255) } } elseif ($sitecheck -ge 256 -and $sitecheck -le 399 ) { if ($sitestring.substring(2,1) -eq '0')
{
$global:thirdoct =$sitestring.substring(3,1)
}
else
{
$global:thirdoct =$sitestring.substring(2,2)
}
}
elseif ($sitecheck -ge 401 -and$sitecheck -le 499)
{
$global:thirdoct = 100+$sitestring.substring(2,2)
}
elseif ($sitecheck -ge 501 -and$sitecheck -le 799)
{
if ($sitestring.substring(2,1) -eq '0') {$global:thirdoct = $sitestring.substring(3,1) } else {$global:thirdoct = $sitestring.substring(2,2) } } elseif ($sitecheck -eq 1202)
{
$global:thirdoct =$sitestring.substring(1,3)
}
elseif ($sitecheck -ge 5000 -and$sitecheck -le 9999)
{
$site5000 = [int]$sitestring.substring(1,3)
if ($site5000 -le 255) { if ($sitestring.substring(1,2) -eq '00')
{
$global:thirdoct =$sitestring.substring(3,1)
}
elseif ($sitestring.substring(1,1) -eq '0') {$global:thirdoct = $sitestring.substring(2,2) } else {$global:thirdoct = $sitestring.substring(1,3) } } elseif (($site5000 -ge 256 -and $site5000 -le 399) -or ($site5000 -ge 501 -and $site5000 -le 999)) {$global:thirdoct = $sitestring.substring(2,2) } elseif ($site5000 -ge 401 -and $site5000 -le 499) {$global:thirdoct = 100+$sitestring.substring(2,2) } }  • if you do a modulus division by 100 you don't need to distinguish between one or two places $global:thirdoct = [int]$sitestring % 100 And Id use a switch instead of the nested if/elseif to handle the exceptions and leave the rest to the default. – LotPings Mar 22 '19 at 3:13 2 Answers Let's start with a table of inputs and outputs, because it may not be exactly the same as what you expect from your script. The first column is the input range, the other column is the transformation applied for those inputs. 0000-0255 self 0256-0399 last2 0400 undefined 0401-0499 -300 0500 undefined 0501-0799 last2 0800-1201 undefined 1202 last3 1203-4999 undefined 5000-5255 last3 5256-5399 last2 5400-5500 undefined 5501-5999 last2 6000+ same as 5000-5999  The best way to manage these cases is with a data structure. A switch statement can work too but there's going to be a lot of repetition, and you really want something that lets you focus on the data. In this version, the ranges are an array of arrays. Each inner array is a lower bound, an upper bound, and a script block that transforms input to output. As a special case, a negative result from the script block means "restart the conversion with this new value (after making it positive)"—this lets us reuse the low patterns for most of 5000-5999, and then reuse the 5000-5999 pattern for 6000-9999. We simply traverse the array looking matches, and apply the transforms from matching rows. (There's only ever one matching row per call to convert, in this example, but the code allows overlapping ranges; matching transforms will apply in order). $ranges = @(
@(    0,  255, { $_ } ), @( 256, 399, {$_ % 100 }            ),
@(  401,  499, { $_ - 300 } ), @( 501, 799, {$_ % 100 }            ),
@( 1202, 1202, { $_ % 1000 } ), @( 5000, 5399, { -($_ % 1000) }        ), # redo using last 3 digits
@( 5501, 5999, { -($_ % 1000) } ), @( 6000, 9999, { -($_ % 1000 + 5000) } )  # redo using 5000 + last 3
)

$convert = {$_ = [int]$args[0]$out = -1
foreach ($range in$ranges) {
if ($_ -ge$range[0] -and $_ -le$range[1]) {
$out = &$range[2]
# negative result means re-do the conversion with a new value
if ($out -lt 0) {$out = & $convert ($out * -1) }
}
}
return $out }$site = Read-Host 'enter the site code: '
$octet = &$convert $site if ($octet -ge 0) {
echo $octet } else { echo "error -$site has no defined octet"
}

• Nice one +1, I do see a difference in repetitions to a switch as there you can break if a condition is met while the foreach will iterate inevitable through all (non matching) ranges. – LotPings Mar 22 '19 at 10:14
• that's entirely optional: you could move return $out inside of the loop and return on the first match. I think this way is slightly better: makes it easy to have one-off exceptions, like if 400-499 does one thing but 411 and 455 do something different, have a 400-499 range, and below it specific overrides for the exceptions. – Oh My Goodness Mar 22 '19 at 11:09 See the following as an incomplete and untested template, ATM too tired to distinguish between parens and curly braces. $global:site = Read-Host 'What is the 4 digit site number?' #must include leading zeros
[int]$global:sitecheck=$site #need for comparasion operators
[string]$global:sitestring=$site

switch ($sitecheck){ {$_ -le 255}     {$global:thirdoct =$_ % 1000;Break}

{$_ -ge 256 -and$_ -le 399}     { ;Break}

{$_ -ge 401 -and$_ -le 499}      {$global:thirdoct = 100 +$_ % 100;Break}

{$_ -eq 1202} {$global:thirdoct = $_ % 1000;Break} {$_ -ge 501 -and
$_ -le 799} {} {$_ -ge 5000 -and
$_ -le 9999} { ;Break} default { } } "Site:{0} thirdOct: {1}" -f$Sitecheck,\$thirdOct
`