user11963593
user11963593

Reputation:

Appending objects to arrays in Powershell

I have the following code:

$DataType = "X,Y,Z"
$Data = "1,2,3"
$Table = @()
for ($i = 0; $i -le ($DataType.Count-1); $i++)
{
    $Properties = @{$DataType[$i]=$Data[$i]}
    $Object = New-Object -TypeName PSCustomObject -Property $Properties
    $Table += $Object
}
$Table | Format-Table -AutoSize

I get this output:

X
-
1

What I would like to get is:

X  Y  Z
-   -   -
1  2  3 

Thanks for your help!

Upvotes: 1

Views: 576

Answers (5)

Vish
Vish

Reputation: 466

One solution to this problem (if the inputs were two separate arrays):

$DataType = @( 'X','Y','Z' )
$Data     = @( '1','2','3' )
$Table    = New-Object psobject

for ($i = 0; $i -le ( $DataType.Count-1 ); $i++)
{
    $Table | Add-Member -Name "$( $DataType[$i] )" -Value ( $Data[$i] ) -MemberType NoteProperty
}

$Table

Upvotes: 0

iRon
iRon

Reputation: 23830

Cutting a long story short:

$DataType, $Data | ConvertFrom-Csv

X Y Z
- - -
1 2 3

Ok, it needs a little explanation:
PowerShell will automatically unroll the array of strings ($DataType, $Data) and supply it as individual line items to the pipeline. The ConvertFrom-Csv cmdlet supports supplying the input table through the pipeline as separate lines (strings).

Upvotes: 4

AdminOfThings
AdminOfThings

Reputation: 25031

You can do the following instead:

$DataType = "X","Y","Z"
$Data = 1,2,3
$hash = [ordered]@{}
for ($i = 0; $i -lt $DataType.Count; $i++) {
    $hash.Add($DataType[$i],$Data[$i])
}
$table = [pscustomobject]$hash

Explanation:

The code creates two collections, $DataType and $Data, of three items. $hash is an ordered hash table. [ordered] is used to preserve the order at which key-value pairs are added to the hash table. Since $hash is the object type hashtable, it contains the .Add(key,value) method for adding key-value pairs.

Since the [pscustomobject] type accelerator can be cast on a hash table, we can simply use the syntax [pscustomobject]$hash to create a new object.


If we consider your attempt, your variables are actually single strings rather than collections. Surrounding a value with quotes causes PowerShell to expand the inner contents as a string. When you index a string rather than a collection, you index the characters in the string rather than the entire item. You need to quote the individual elements between the commas so that the , acts as a separator rather than part of the string. You can see this behavior in the code below:

# DataType as a string
$DataType = "X,Y,Z"
$DataType[1]
,

# DataType as an array or collection
$DataType = "X","Y","Z"
$DataType[1]
Y

If you receive your data from another output in the current format, you can manipulate using $DataType = $DataType.Split(',') in order to create a collection. Alternatively you can treat the data as comma-separated and use the Import-Csv or ConvertFrom-Csv commands as in iRon's answer provided you order your strings properly.

Inside of your loop, you are adding three new objects to your collection $table rather than creating one object with three properties. $table += $Object creates an array called $table that appends a new item to the previous list from $table. If this was your original intention, you can view your collection by running $table | Format-List once you fix your $DataType and $Data variables.

When a collection is enumerated, the default table view displays the properties of the first object in a collection. Any succeeding objects will only display values for the first object's matching properties. So if object1 has properties X and Y and object2 has properties Y and Z, the console will only display values for properties X and Y for both objects. Format-List overrides this view and displays all properties of all objects. See below for an example of this behavior:

$obj1

X Y
- -
1 2


$obj2

Y Z
- -
3 4

$array = $obj1,$obj2

# Table View
$array

X Y
- -
1 2
  3

# List View
$array | Format-List

X : 1
Y : 2

Y : 3
Z : 4

Upvotes: 2

Lance U. Matthews
Lance U. Matthews

Reputation: 16612

It seems that you want to create a single object with a property for each value in the arrays $DataType/$Data, but the problems are...

  • Neither $DataType nor $Data are arrays.
  • By creating your object inside the for loop you will create one object per iteration.
  • Since $DataType is a scalar variable $DataType.Count returns 1. Ordinarily, testing for $DataType.Count-1 would mean the loop never gets entered, but by the grace of using -le (so 0 -le 0 returns $true) instead of -lt, it does for exactly one iteration. Thus, you do get your single result object, but with only the first property created.

To fix this, let's create $DataType and $Data as arrays, as well as creating one set of properties before the loop to be used to create one result object after the loop... ...

$DataType = "X,Y,Z" -split ','
$Data = "1,2,3" -split ','
$Properties = @{}
for ($i = 0; $i -lt $DataType.Count; $i++)
{
    $Properties[$DataType[$i]] = $Data[$i]
}
New-Object -TypeName PSCustomObject -Property $Properties | Format-Table -AutoSize

You'll also notice that $i -le ($DataType.Count-1) has been simplified to $i -lt $DataType.Count. On my system the above code outputs...

Y Z X
- - -
2 3 1

The properties are correct, but the order is not what you wanted. This is because Hashtable instances, such as $Properties, have no ordering among their keys. To ensure that the properties are in the order you specified in the question, on PowerShell 3.0 and above you can use this to preserve insertion order...

$Properties = [Ordered] @{}

Upvotes: 1

Euler
Euler

Reputation: 322

What if you initialized $Table as an appendable like so:

$Table = New-Object System.Collections.ArrayList
for ($i = 0; $i -le ($DataType.Count-1); $i++)
{
    $Properties = @{$DataType[$i]=$Data[$i]}
    $Object = New-Object -TypeName PSCustomObject -Property $Properties
    $Table.Add ( $Object )
}

Reformat your logic as needed.

Upvotes: 0

Related Questions