String being treated as Int

This topic contains 7 replies, has 3 voices, and was last updated by Profile photo of Rob Simmers Rob Simmers 1 year, 11 months ago.

  • Author
    Posts
  • #26716
    Profile photo of Isaac Garvin
    Isaac Garvin
    Participant

    I have a function where a parameter variable declared as a [string[]]. The problem is I notice that if I feed it numbers with leading zeroes (e.g. 00426) it will trim those zeroes like it was an Int. I thought the point of making it a string is so that it won't fiddle with input like that? Any way to avoid this? The only way I know to workaround is by wrapping input in quotes:

    Bad:
    My-Function -Parameter 0002440, 000953

    Good:
    My-Function -Parameter "0002440", "000953".

    Parameter:

            [Parameter(Mandatory=$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0)]
            [ValidateNotNullOrEmpty()]
            [string[]]$Device,  
    
  • #26718
    Profile photo of Don Jones
    Don Jones
    Keymaster

    Well, technically, a series of digits not in quotes is a number. The zeros don't get "trimmed," they're just not significant, so they're not stored – and that happens before the coercion into string. Put the digits in quotes if you want them treated as a string from the outset – as you noticed.

  • #26731
    Profile photo of Isaac Garvin
    Isaac Garvin
    Participant

    So there's no way to force it to interpret them as significant such as with adding something to the console profile?

    I just was hoping to avoid it since this script will be used by others and it will be one less "gotcha" they will have to deal with, plus it doesn't appear it would translate as easily from other sources, such as pulling those values from CSVs. If that's the nature of the beast however then I'll just have to work around it but I feel it'll end up being rather kludgy.

  • #26741
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    You can account for it in your function and pad the values passed to the function by using string format. In the example below we have an array of integers and an array of strings.

    $array1 = 1234,64322,23523
    
    $array2 = "1234","64322","23523"
    
    foreach ($item in $array1) {
        "Array1 - ToString Method: {0}" -f $item.ToString("D8")
        "Array1 - Format Method: {0:D8}" -f $item
    }
    
    foreach ($item in $array2) {
        "Array2 - ToString Method: {0}" -f $item.ToString("D8")
        "Array2 - Format Method: {0:D8}" -f $item
    }
    

    When processing the "string".ToString("D8") Powershell complained:

    Cannot find an overload for "ToString" and the argument count: "1".
    At line:1 char:1
    + $test.ToString("D8")
    + ~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (:) [], MethodException
        + FullyQualifiedErrorId : MethodCountCouldNotFindBest
    

    So, I would use the string.Format method and pad the appropriate amount on $Device and then it would not matter if they passed strings or numbers, it would be 7 characters when being processed:

    foreach ( $item in $Device ) {
        $item = "{0:D7}" -f $item
        "Doing something with device {0}" -f $item
    }
    
  • #26775
    Profile photo of Isaac Garvin
    Isaac Garvin
    Participant

    Hi Rob,

    Perhaps I'm not interpreting correctly. I tried the string.Format option for the string array but no changes occurred but it was able to pad the zeroes to the int array. MSDN documentation also says D format specifier is integral only, so I'd be surprised that it would work on a string. Any ideas?

  • #26778
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    This worked for me:

    function Test-It {
        param(
            [Parameter(Mandatory=$true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName=$true,
            Position=0)]
            [ValidateNotNullOrEmpty()]
            [string[]]$Devices
        )
        begin{}
        process{
            foreach ($Device in $Devices) {
                $device = ([int]$Device).ToString("0000000")
                "Processing {0}" -f $device
            } 
        }
        end{}
    }
    
    Test-It -Devices 1234,64322,23523
    Test-It -Devices "1234","64322","23523"
    

    Output:

    Processing 0001234
    Processing 0064322
    Processing 0023523
    Processing 0001234
    Processing 0064322
    Processing 0023523
    

    I also added ValueFromPipeline = $true which would allow you to pipe to the function:

    "1234","64322","23523" | Test-It
    
  • #26781
    Profile photo of Isaac Garvin
    Isaac Garvin
    Participant

    Great, that works just fine! Only one minor thing I have to ask, and that's how to get it to preserve the changes by placing the newly formatted values back into the original string[]$Device array. I noticed that it never actually places the results back into the array, and just displays them with the new format and then promptly discards the results.

  • #26782
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    It's really a question of what you want to return. Typically, you want to return a PSObject.

    function Test-It {
        param(
            [Parameter(Mandatory=$true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName=$true,
            Position=0)]
            [ValidateNotNullOrEmpty()]
            [string[]]$Devices
        )
        begin{}
        process{
            
            $results = foreach ($Device in $Devices) {
                $device = ([int]$Device).ToString("0000000")
                New-Object -TypeName PSObject -Property @{Device=$device;Status="Success"}
            } 
        }
        end{$results}
    }
    
    Test-It -Devices 1234,64322,23523 | Format-Table -AutoSize
    

    The function would return this:

    Status  Device 
    ------  ------ 
    Success 0001234
    Success 0064322
    Success 0023523
    

You must be logged in to reply to this topic.