4
\$\begingroup\$

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)
 }
 }
200_success
146k22 gold badges190 silver badges478 bronze badges
asked Mar 22, 2019 at 1:43
\$\endgroup\$
1
  • \$\begingroup\$ 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 I`d use a switch instead of the nested if/elseif to handle the exceptions and leave the rest to the default. \$\endgroup\$ Commented Mar 22, 2019 at 3:13

2 Answers 2

4
\$\begingroup\$

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"
}
answered Mar 22, 2019 at 6:07
\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Mar 22, 2019 at 10:14
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Mar 22, 2019 at 11:09
0
\$\begingroup\$

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
answered Mar 22, 2019 at 4:04
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.