Import-CSV, change incoming from string to int. Solved; is there another way?

Welcome Forums General PowerShell Q&A Import-CSV, change incoming from string to int. Solved; is there another way?

This topic contains 6 replies, has 3 voices, and was last updated by

 
Participant
4 days, 19 hours ago.

  • Author
    Posts
  • #128524

    Participant
    Points: 48
    Rank: Member

    Team PowerShell,

    Is there a way to do this better?

    Take incoming data from a CSV, validate if a value can be an integer type, and change to integer.

    Use case is to deploy a VMware guest using new-vm command.  Some parameters specify data type as int32

    My csv file

    Properties Value
    host vmhost1.company.com
    vm_name accounting_test
    ds_name dev_ds_1
    vm_network vlan243
    clone_from server-wk12-60gb
    template template-w2k12r2-50gb
    memory_gb 4
    num_cpu 2
    cores_per_socket 2
    My hack, hacked from obscure, dusty places located in Google Land:
    $csv = import-csv "C:\temp\vm_build.csv"
    
    foreach ($item in $csv){
    
        if( ($item.value -as [int]) -ne $null){      # evaluate if a string can be an integer
    
        [int32]$item.value = $item.value}            # change to an integer
    
    New-Variable $item.properties $item.value        # create variables, populate with value
    }

     

    Happily, I get:

     

    PS C:\powershell> $num_cpu.gettype()
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     Int32                                    System.ValueType
    
    
    PS C:\powershell> $template.gettype()
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     String                                   System.Object
  • #128533

    Participant
    Points: 305
    Helping Hand
    Rank: Contributor

    You can do something like this:

    $csv = @()
    $csv += [pscustomobject]@{Properties='host';Value='vmhost1.company.com'}
    $csv += [pscustomobject]@{Properties='memory_gb';Value=4}
    $csv += [pscustomobject]@{Properties='num_cpu';Value=2}
    
    
    $csv | Select Properties, @{Name='Value';Expression={if($_.Value -match "^[\d\.]+$"){[int]$_.Value}else{$_.Value}}}
    

    Output:

    PS C:\Users\Rob> $csv[0].Value.GetType()
    
    
    IsPublic IsSerial Name                                     BaseType                                                                                                                                         
    -------- -------- ----                                     --------                                                                                                                                         
    True     True     String                                   System.Object                                                                                                                                    
    
    
    
    PS C:\Users\Rob> $csv[1].Value.GetType()
    
    
    IsPublic IsSerial Name                                     BaseType                                                                                                                                         
    -------- -------- ----                                     --------                                                                                                                                         
    True     True     Int32                                    System.ValueType                                                                                                                                 
    

    But my question is why you want to do it. It's not really necessary in Powershell as it will do most of these conversions occur dynamically, you don't need to implicitly tell Powershell it needs to be an INT. Take this as an example:

    function Test-It {
        param (
            [int]$MyNumber
        )
    
        $MyNumber.GetType()
    }
    
    $string = "123"
    $string.GetType()
    Test-It -MyNumber $string
    

    You'll see we define 123 as string, the GetType() will tell us it's a string, but when we pass it as a parameter that is expecting an integer, it will do the conversion to INT there and return GetType() as an INT.

    PS C:\Users\Rob> $string = "123"
    
    
    PS C:\Users\Rob> $string.GetType()
    
    
    IsPublic IsSerial Name                                     BaseType                                                                                                                                         
    -------- -------- ----                                     --------                                                                                                                                         
    True     True     String                                   System.Object                                                                                                                                    
    
    
    
    PS C:\Users\Rob> Test-It -MyNumber $string
    
    IsPublic IsSerial Name                                     BaseType                                                                                                                                         
    -------- -------- ----                                     --------                                                                                                                                         
    True     True     Int32                                    System.ValueType                                                                                                                                 
    
  • #128547

    Participant
    Points: 275
    Helping Hand
    Rank: Contributor

    This is quite close to being one of the better ways to go already, you just need to clean up your code and double check your logic. As Rob mentions, you could also store things neatly in a hashtable or a similar construct, which will make it easier to find your values again. Programmatically generating names of variables is often a bit of a tiring exercise, because you will have to then find them again. Best to keep them all in one place, in one variable.

    # Create empty hashtable as a container
    $Table = @{}
    foreach ($Item in $csv) {
        $IntValue = $Item.Value -as [int]
        # If the cast fails, $IntValue will be $null, and this statement will be skipped ($null casts to $false)
        if ($IntValue) {
            $Table.Add($Item.Properties, $Item.Value -as [int]
        }
    }
    
  • #128653

    Participant
    Points: 48
    Rank: Member

    Hi, Joel and Rob,

    Thank you for your responses.  I'm looking them over, as to learn.

    I believe the root action of import-csv is everything comes in as a string; integer-shaped-objects are thusly not typed as [int].  To meet PoweCLI new-vm parameter requirements, e.g. [-CoresPerSocket Int32], [-NumCpu Int32],  I gotta do some type manipulation.

    Dynamically creating the variables from the CSV will map to the hash table that splats the parameters;  perhaps some CSV configs have E and F drive, some CPU and memory requirements will differ, etc.  Programming logic will IF for additional items, and call functions accordingly.

    function import-VMParameters {
    Import-CSV -path

    New-Variable  #validate for int, where int is required, create variables

    # Call connect-VCenter function

    }

    function connect-VCenter{

    log in

    # call new-vmbuild function

    }

    function new-vmbuild{
    if ($clone_from){
    $build=@{
    VMHost=$host
    name=$vm_name
    Datastore=$ds_name
    networkname=$vm_network
    VM=$clone_from
    }
    new-vm @build
    # call set_CpuMem function
    }
    ...and so it goes.
  • #128656

    Participant
    Points: 305
    Helping Hand
    Rank: Contributor

    I believe the root action of import-csv is everything comes in as a string; integer-shaped-objects are thusly not typed as [int]. To meet PoweCLI new-vm parameter requirements, e.g. [-CoresPerSocket Int32], [-NumCpu Int32], I gotta do some type manipulation.

    You are correct, everything imported from a CSV is a string. Look at the second example I provided, you don't need to do type conversions. If the input can be converted to the type (e.g. [int]), Powershell will do it for you without explicitly defining the type before it's passed. It doesn't hurt anything, but you are doing unnecessary work IMHO as the New-VM function will do the same conversion you are doing manually. In both examples below, NumCpu is passed a string, but the conversion is handled by defining it as [int] in the function params:

    New-VM -NumCpu "1"
    New-VM -NumCpu '1'
    
  • #128658

    Participant
    Points: 275
    Helping Hand
    Rank: Contributor

    You mention "parameter requirements" but if we're being honest those are somewhat loose in PowerShell functions. Even if your value is a string, as long as it can be parsed as a number PowerShell will automatically convert it to the proper value when you pass into a numeric-typed function argument.

  • #128680

    Participant
    Points: 48
    Rank: Member

    Pfffft, not work so hard?  Sign me up! 🙂

    Thank you for clarifying that POSH will do the conversion to the type.   That does make it easier.  I'll tuck away the manual "evaluate for int" for future use.

    Cheers,

    Seth

You must be logged in to reply to this topic.