0

I am trying to validate a string of what should be comma separated integer values to use as success codes. If I start with an array with some invalid values, like this...

$test = @(0, 100, -200, 3.3, 'string')
foreach ($item in $test) {
    if ($item -isnot [Int]) {
        Write-Host "![Int] $item"
    } else {
        Write-Host "[Int] $item"
    }
}

I get the expected results, the first three are [Int] and the last two are not.

However, if I start from a string and build the array, like this

$testString = '0, 100, -200, 3.3, string'
$test = $testString.Split(',').Trim()

then all tests are not [Int], which makes sense because the array will be strings. I need to cast to [Int] first. But when I do, like this

$testString = '0, 100, -200, 3.3, string'
$test = $testString.Split(',').Trim()
foreach ($item in $test) {
    if ($item -as [Int] -isnot [Int]) {
        Write-Host "![Int] $item"
    } else {
        Write-Host "[Int] $item"
    }
}

3.3 is returning as [Int]. I found this which clues me in that casting is too smart for what I am trying to do it seems. So, IS there a way to cast a decimal to int and get a failure, or do I need two conditionals, one to cast and catch the true strings, and one to look for . and thus find the decimals? Or perhaps to compare the value of [Int] with a cast to [Single] like this?

$testString = '0, 100, -200, 3.3, string'
$test = $testString.Split(',').Trim()
foreach ($item in $test) {
    if ($item -as [Int32] -isnot [Int32]) {
        Write-Host "![Int] $item"
    } else {
        Write-Host "[Int] $item"
    }
}

This is the most elegant way I have managed to get to work, but I wonder if there is actually a better approach? I also tried if ([Int]$item -isnot [Int]) {, which is I think an actual Cast, while -as is technically Coercion, and that still fails to catch the decimal, while throwing an error on the string. So I am left thinking I understand Casting vs Coercion better than I did, and the working code above is as good as it gets. Hoping for some verification or refutation on both.

EDIT: Actually, that only works because my decimal is not .0. If I want to flag 3.0 as an error as well as 3.3 I really need to check for the decimal instead, like so.

if (($item -as [Int] -isnot [Int]) -or ($item -like '*.*')) {
Gordon
  • 6,257
  • 6
  • 36
  • 89
  • You can use regular expressions to test if a string is an `[int]` or not: [Check if string contains numeric value in PowerShell](https://stackoverflow.com/questions/51171410/check-if-string-contains-numeric-value-in-powershell/51172049#51172049) – boxdog May 13 '22 at 08:58
  • @boxdog I think that runs afoul of Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems. I have done that many a time, and I am trying to get better at keeping things simple, and using RegEx where there is no other (good) option. Though this may be simple enough that RegEx is actual a good option. :) – Gordon May 13 '22 at 09:08
  • @Gordon You're trying to assess whether a string contains a particular pattern of text, this is literally the only use case where regex is appropriate and good :) Alternatively, use the `+` operator: `(+"3.0").GetType()` – Mathias R. Jessen May 13 '22 at 09:44
  • 2
    There's always `Int.TryParse`. PowerShell can do the same things .NET can. `'0, 100, -200, 3.3, string' -split ',' |% { [int] $i = 0; if ([int]::TryParse($_, [ref] $i)) { $i } }`. – Jeroen Mostert May 13 '22 at 10:13

0 Answers0