red888
red888

Reputation: 31530

Joining the properties of an array of objects the powershell way

I have an array of objects.

I would like to join the properties of these objects and normally I would simply do this:

$Objs.prop1 -join(',')
$Objs.prop2 -join(',')
$Objs.prop3 -join(',')

But for this case I want to do some manipulation of the properties before I join them which means I have to (hoping there is a more powershelly way) do this:

$ArrObj = @()
foreach ($obj in $objs) {
    $calcProp1 = [math]::Round($obj.prop1 / 1MB)
    $calcProp2 = [math]::Round($obj.prop2 / 1MB)
    $calcProp3 = [math]::Round($obj.prop3 / 1MB)
    $ArrObj += [string]$clacProp1+','+$clacProp2+','+$clacProp3}
}

Is there a nifty PS shortcut for doing something like this?

Upvotes: 2

Views: 10554

Answers (2)

MisterSeajay
MisterSeajay

Reputation: 4659

Firstly, one pretty direct way to get an object formatted with commas between properties is to use the ConvertTo-Csv cmdlet, available from PSv3:

$Objs | Select prop1,prop2,prop3 | ConvertTo-Csv -NoTypeInformation

Next, if you want to convert properties, you could use Select-Object with expressions:

$Objs | Select-Object -Property `
  @{Label="Calc1";Value={[math]::Round($_.prop1 / 1MB)}} `
  @{Label="Calc2";Value={[math]::Round($_.prop2 / 1MB)}} `
  @{Label="Calc3";Value={[math]::Round($_.prop3 / 1MB)}} `
  | ConvertTo-Csv -NoTypeInformation

A different tack could be to extend your original object using the Add-Member cmdlet. You can add new calculated properties:

$Objs | Add-Member -MemberType ScriptProperty -Name "Calc1" -Value {[math]::Round($_.prop1 / 1MB)}}
$Objs | Add-Member -MemberType ScriptProperty -Name "Calc2" -Value {[math]::Round($_.prop2 / 1MB)}}
$Objs | Add-Member -MemberType ScriptProperty -Name "Calc3" -Value {[math]::Round($_.prop3 / 1MB)}}

This will give you three additional properties on $Objs called Calc1|2|3, which you can then convert to CSV format:

$Objs | Select Calc1,Calc2,Calc3 | ConvertTo-Csv -NoTypeInformation

Upvotes: 0

Ansgar Wiechers
Ansgar Wiechers

Reputation: 200203

Your two code snippets do different things. Assuming that you don't want want to join the same property across all objects as in your first example, but rather join properties per object as in your second example this should do what you want:

$objs | ForEach-Object {
  ($_.PSObject.Properties | ForEach-Object {
    [math]::Round($_.Value / 1MB)
  }) -join ','
}

If you want just selected properties you can filter them by name:

$propertyNames = 'prop1', 'prop2', 'prop3'

$objs | ForEach-Object {
  ($_.PSObject.Properties | Where-Object {
    $propertyNames -contains $_.Name
  } | ForEach-Object {
    [math]::Round($_.Value / 1MB)
  }) -join ','
}

or use a custom function for manipulating the values before joining them:

function Convert-PropertyValue {
  [CmdletBinding()]
  Param(
    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
    [Management.Automation.PSPropertyInfo]$Property
  )

  Process {
    switch ($Property.Name) {
      'prop1' { [math]::Round($Property.Value / 1MB) }
      'prop2' { $Property.Value + 42 }
      'prop3' { 'Value: {0:d3}' -f $Property.Value }
      default { $Property.Value }
    }
  }
}

$objs | ForEach-Object {
  ($_.PSObject.Properties | Convert-PropertyValue) -join ','
}

Upvotes: 3

Related Questions