Powershell split csv column value into two seperate column values - powershell
Lets say I have a csv file with 5 records (in reality it's about 80,000).
Csv File:
column1,columnICareAbout,column2
someRandomValue,John Doe - Generic,someRandomValue
someRandomValue,Captain Teemo - pocketPet,someRandomValue
someRandomValue,Pinky Brain - Fictional,someRandomValue
someRandomValue,Miyamoto Musashi - swordsman,someRandomValue
someRandomValue,Kato yasunori - troubleMaker - Extreme,someRandomValue
Given the following code:
Note: I know the array values are correct, I just need to know how to loop through the array in the expression.
$firstNames = #()
$firstNameCounter = 0
$lastNames = #()
$lastNameCounter = 0
Import-Csv $file1 |
foreach {
$firstNames += $_.Description.split(" ")[0]
$lastNames += $_.Description.split(" ")[1]
}
Import-Csv $file1 |
Select-Object *, #{n='First Name'; e={$firstNames[$firstNameCounter];$script:firstNameCounter++}}, #{n='Last Name'; e={$lastNames[$lastNameCounter];$script:lastNameCounter++}} |
Export-Csv "testresults.csv" -NoTypeInformation -Force
I only get the first element in the array every time. So the end result file looks like this
column1,columnICareAbout,column2,First Name,Last Name
someRandomValue,John Doe - Generic,someRandomValue,John,Doe
someRandomValue,Captain Teemo - pocketPet,someRandomValue,John,Doe
someRandomValue,Pinky Brain - Fictional,someRandomValue,John,Doe
someRandomValue,Miyamoto Musashi - swordsman,someRandomValue,John,Doe
someRandomValue,Kato yasunori - troubleMaker - Extreme,someRandomValue,John,Doe
I want the file to look like this
column1,columnICareAbout,column2,First Name,Last Name
someRandomValue,John Doe - Generic,someRandomValue,John,Doe
someRandomValue,Captain Teemo - pocketPet,someRandomValue,Captain,Teemo
someRandomValue,Pinky Brain - Fictional,someRandomValue,Pinky,Brain
someRandomValue,Miyamoto Musashi - swordsman,someRandomValue,Miyamoto,Musashi
someRandomValue,Kato yasunori - troubleMaker - Extreme,someRandomValue,Kato,yasunori
Can Anyone tell me what I'm doing wrong?
Import-Csv $file1 |
foreach {
$firstName = $_.Description.split(" ")[0]
$lastName = $_.Description.split(" ")[1]
$_ | Add-Member -MemberType NoteProperty -Name FirstName -Value $firstName
$_ | Add-Member -MemberType NoteProperty -Name LastName -Value $lastName
} | Export-Csv 'results.csv'
Add-Member commands will append the 2 columns FirstName and LastName
Related
How to Add a new Column with Special Character Count in PowerShell?
Example Col Example Col2 Cell 1 words : words2 = Cell 3 examplewords:continued Hi! I am trying to create a third column where I can get the amount of colons and equal signs in the Example Col2. For example, the first row of the third added column would be 2, and the second row would be 1. This is what I have tried so far, but I have had no luck. I am really new to PowerShell, so thanks for the help!
Can't say it'd be more efficient than Theo's solution above, but if you wanted an alternative to Select-String matching you could use replace with length property. $data = $( [PSCustomObject]#{ 'Example Col' = 'Cell 1'; 'Example Col2' = 'words : words2 =' } [PSCustomObject]#{ 'Example Col' = 'Cell 3'; 'Example Col2' = 'examplewords:continued' } ) $data | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Name 'Example Col3' -Value ($_.'Example Col2' -replace '[^:=]').Length }
If your data comes from a csv file like this: "Example Col","Example Col2" "Cell 1","words : words2 =" "Cell 3","examplewords:continued" Then you could do $data = Import-Csv -Path 'X:\Path\example.csv' | Select-Object *, #{Name = 'Example Col3'; Expression = { # (Select-String -InputObject $_.'Example Col2' -Pattern "[:=]" -AllMatches).Matches.Count} # a faster option: [regex]::Matches($_.'Example Col2', '[:=]').Groups.Count } } $data If however you want to use Add-Member on the objects held in an array in memory, you could loop trhough like $data | ForEach-Object { # $count = (Select-String -InputObject $_.'Example Col2' -Pattern "[:=]" -AllMatches).Matches.Count # a faster option: $count = [regex]::Matches($_.'Example Col2', '[:=]').Groups.Count $_ | Add-Member -MemberType NoteProperty -Name 'Example Col3' -Value $count } $data Result: Example Col Example Col2 Example Col3 ----------- ------------ ------------ Cell 1 words : words2 = 2 Cell 3 examplewords:continued 1
PowerShell ForEach-Object Only Matching first Value passed
I am writing a PowerShell script that reads a CSV file in the following format: A,B,ProgrammeSeller,D,E,F,G,H,I,J,K,L,M,N,O,P,SellerCode,Date,Seller,Currency1,InvoiceAmmount,DiscountAmount,PurchasePrice,TotalsCurrency,TotalInvoiceAmmount,TotalDiscountAmmount,TotalPurchasePrice,Reviewed,AC,Signed ,,PROGRAMME,,,,,,,,,,,,,,380,09/12/2021,SELLER_BE,EUR,2813828.46,17594.22,2796234.24,EUR,0,0,0,Reviewed by:,,Signed: ,,PROGRAMME,,,,,,,,,,,,,,383,09/12/2021,SELLER_DE,EUR,3287812.58,17595.8,3270216.78,EUR,0,0,0,Reviewed by:,,Signed: ,,PROGRAMME,,,,,,,,,,,,,,383,09/12/2021,SELLER_DE,USD,1520725.4,11428.98,1509296.42,USD,0,0,0,Reviewed by:,,Signed: ,,PROGRAMME,,,,,,,,,,,,,,381,09/12/2021,SELLER_DK,DKK,6047281.25,26163.13,6021118.12,DKK,0,0,0,Reviewed by:,,Signed: ,,PROGRAMME,,,,,,,,,,,,,,381,09/12/2021,SELLER_DK,EUR,11376479.39,39580.28,11336899.11,EUR,0,0,0,Reviewed by:,,Signed: ,,PROGRAMME,,,,,,,,,,,,,,381,09/12/2021,SELLER_DK,USD,12571895.13,71198.51,12500696.62,USD,0,0,0,Reviewed by:,,Signed: And I want to group and sum 3 of the columns (InvoiceAmmount,DiscountAmount,PurchasePrice), and update the columns (TotalInvoiceAmmount,TotalDiscountAmmount,TotalPurchasePrice) on a new file which is a copy of the original but with the columns (TotalInvoiceAmmount,TotalDiscountAmmount,TotalPurchasePrice) updated with the values of the sums whenever there is a match on Currency value. I have written the following script: $csvFile = Import-Csv "C:\OutputFiles\OTC_Purchase_Report_EUProgramme_20220420_TMP.csv" $csvFinal = "C:\OutputFiles\OTC_Purchase_Report_EUProgramme_20220420.csv" function WriteTotalsToCSV { $totals | ForEach-Object { $totalCurrency = $_.Currency $totalInvoiceAmmount = $_.TotalInvoiceAmmount $totalDiscountAmmount = $_.TotalDiscountAmmount $totalPurchasePrice = $_.TotalPurchasePrice $csvFile | ForEach-Object { if($_.Currency1 -eq $totalCurrency){ $_.TotalsCurrency = $totalCurrency $_.TotalInvoiceAmmount = $totalInvoiceAmmount $_.TotalDiscountAmmount = $totalDiscountAmmount $_.TotalPurchasePrice = $totalPurchasePrice } $_ | Export-Csv $csvFinal -NoTypeInformation -Append } } } function InvoiceAmmountTotals { param ($file) $headers = ($file | Get-Member -MemberType NoteProperty).Name $totals = $file | Select-Object $headers | Group-Object Currency1 foreach ($total in $totals) { $showtotal = New-Object System.Management.Automation.PsObject $showtotal | Add-Member NoteProperty Currency $total.Name $showtotal | Add-Member NoteProperty TotalInvoiceAmmount ($total.Group | Measure-Object -Sum InvoiceAmmount).Sum $showtotal | Add-Member NoteProperty TotalDiscountAmmount ($total.Group | Measure-Object -Sum DiscountAmount).Sum $showtotal | Add-Member NoteProperty TotalPurchasePrice ($total.Group | Measure-Object -Sum PurchasePrice).Sum $showtotal } } #execution $totals = InvoiceAmmountTotals $csvFile WriteTotalsToCSV The script is grouping and summing the totals as expected but when it writes the new CSV file it is supposed to update the columns based on a match of column Currency1, but it is only doing this for the first match (in this case EUR) and ignoring the remaining matches i.e.: "A","B","ProgrammeSeller","D","E","F","G","H","I","J","K","L","M","N","O","P","SellerCode","Date","Seller","Currency1","InvoiceAmmount","DiscountAmount","PurchasePrice","TotalsCurrency","TotalInvoiceAmmount","TotalDiscountAmmount","TotalPurchasePrice","Reviewed","AC","Signed" "","","PROGRAMME","","","","","","","","","","","","","","380","09/12/2021","SELLER_BE","EUR","2813828.46","17594.22","2796234.24","EUR","153995434.65","797318.07","153198116.58","Reviewed by:","","Signed:" "","","PROGRAMME","","","","","","","","","","","","","","383","09/12/2021","SELLER_DE","EUR","3287812.58","17595.8","3270216.78","EUR","153995434.65","797318.07","153198116.58","Reviewed by:","","Signed:" "","","PROGRAMME","","","","","","","","","","","","","","383","09/12/2021","SELLER_DE","USD","1520725.4","11428.98","1509296.42","USD","0","0","0","Reviewed by:","","Signed:" "","","PROGRAMME","","","","","","","","","","","","","","381","09/12/2021","SELLER_DK","DKK","6047281.25","26163.13","6021118.12","DKK","0","0","0","Reviewed by:","","Signed:" "","","PROGRAMME","","","","","","","","","","","","","","381","09/12/2021","SELLER_DK","EUR","11376479.39","39580.28","11336899.11","EUR","153995434.65","797318.07","153198116.58","Reviewed by:","","Signed:" "","","PROGRAMME","","","","","","","","","","","","","","381","09/12/2021","SELLER_DK","USD","12571895.13","71198.51","12500696.62","USD","0","0","0","Reviewed by:","","Signed:" Note when column Currency1 value is USD the columns (TotalInvoiceAmmount,TotalDiscountAmmount,TotalPurchasePrice) are not being updated. Any suggestions on where I am going wrong here? Note: The CSV files are much larger, I have added a small number of entries for example purpose Thanks
It looks like you are looping through each line of the csv as many times as you have currencies because you are looping through $totals and inside of each of those loops you are looping through each line of the csv causing duplicates. Loop only once through the csv lines and for each line find the matching currency in your $totals array to update each csv line with. Finally output all at once to Export-Csv function WriteTotalsToCSV { # only one loop through csv lines $csvFile | ForEach-Object { $line = $_ # find matching currency in your $totals array using Where() $totalsMatch = $totals.Where({ $line.Currency1 -eq $_.Currency }) $line.TotalsCurrency = $totalsMatch.Currency $line.TotalInvoiceAmmount = $totalsMatch.TotalInvoiceAmmount $line.TotalDiscountAmmount = $totalsMatch.TotalDiscountAmmount $line.TotalPurchasePrice = $totalsMatch.TotalPurchasePrice $line # collect all the lines first and then export to csv once at the end } | Export-Csv -Path $csvFinal -NoTypeInformation }
CSV misalignment when reading in powershell
I have a CSV with a lot of headers. It ranges from Column A to AZ. I need to read only the first column of the CSV. My issue is the last column (comments) bleeds into the first column when opened in notepad++ and it appears to show that it is apart of Column one (host name). When I run my code it reads it as notepad++ does. I originally convert this from an .xls to a CSV file, is there a way to ensure the last column does not wrap around? #region - Convert to CVS $strFileName = "C:\working\Server Count & Status Check ARC 0219183.xlsx" $strSheetName = 'SCI$' $strProvider = "Provider=Microsoft.ACE.OLEDB.12.0" $strDataSource = "Data Source = $strFileName" $strExtend = "Extended Properties='Excel 8.0;HDR=Yes;IMEX=1';" $strQuery = "Select * from [$strSheetName]" $objConn = New-Object System.Data.OleDb.OleDbConnection("$strProvider;$strDataSource;$strExtend") $sqlCommand = New-Object System.Data.OleDb.OleDbCommand($strQuery) $sqlCommand.Connection = $objConn $objConn.open() $da = New-Object system.Data.OleDb.OleDbDataAdapter($sqlCommand) $dt = New-Object system.Data.datatable $da.fill($dt) $dt | Export-Csv "C:\working\Server Count & Status Check ARC 0219183.csv" - NoTypeInformation $objConn.close() #endregion Original Spreadsheet After trying to gather first column(hostname) $array=#{} $ServerStatus = Get-Content "C:\Users\user\Desktop\Scripts\Linux Server Compare\Server Count & Status Check ARC 0219183.csv" | select -skip 1 | ConvertFrom-Csv -Header "HostName","Business Application","Technical Function","Location","Environment","Day Number","Slot Number","SlotTime","Description","DNS Name","Key Server","Host Name","Public IP","Server Type","Build Date","Builder","Product Name","Serial No","Enclosure","Bay","CPU Model","CPU Socket","CPU Cores","Logical CPU","Memory","Up Time","Kernel Version","OS Type","Version","Patch level","Multipath Disks?","Multipath Checker?","Customized Fsck?","Splx Installed?","Splx Running?","Suse Manager Status","RHN Running ?","RHN Autostart?","Installed Kernel","Available Kernel in /boot","Hardened","Syslog Config correct ?","Syslog Running ?","Patrol Running?","Splunk Installed?","Splunk Running?","Puppet Installed?","Puppet Running?","Centrify Version","VM Tools Installed?","VM Tools Running?","Comments" $ServerStatusObj = new-object Object $ServerStatusObj | add-member -membertype NoteProperty "Name" -value "" $ServerStatusObj | add-member -membertype NoteProperty "Origin" -value "" $array=#{name = $ServerStatus.HostName} foreach ($name in $array.name){ if($name -ne $null){ $Value = $name if($name -ne $null){ $ServerStatusObj.name =$Value $ServerStatusObj.Origin = " Linux Servers" } #End of IF else {Write-Output "null"} Write-Output $ServerStatusObj | export-csv "C:\Users\user\Desktop\Scripts\Linux Server Compare\ServerStatusParse.csv" -notypeinformation -Append }
Powershell - Insert column in between specific columns in csv file
I have 2 csv files First file: firstName,secondName 1234,Value1 2345,Value1 3456,Value1 4567,Value3 7645,Value3 Second file: firstName,fileSplitter,Csv2ColumnOne,Csv2ColumnTwo,Csv2ColumnThree 1234,,1234,abc,Value1 1234,,1234,asd,Value1 3456,,3456,qwe,Value1 4567,,4567,mnb,Value1 I want to insert column secondName in the second file in between columns firstName and fileSplitter. The result should look like this: firstName,secondName,fileSplitter,Csv2ColumnOne,Csv2ColumnTwo,Csv2ColumnThree 1234,Value1,,1234,abc,Value1 1234,Value1,,1234,asd,Value1 3456,Value1,,3456,qwe,Value1 4567,Value3,,4567,mnb,Value1 I'm trying the following code: Function InsertColumnInBetweenColumns { Param ($FirstFileFirstColumnTitle, $firstFile, [string]$1stColumnName, [string]$2ndColumnName, [string]$columnMergedFileBeforeInput) Write-Host "Creating hash table with columns values `"$1stColumnName`" `"$2ndColumnName`" From $OimFileWithMatches" $hashFirstFileTwoColumns = #{} Import-Csv $firstFile | ForEach-Object {$hashFirstFileTwoColumns[$_.$1stColumnName] = $_.$2ndColumnName} Write-Host "Complete." Write-Host "Appending Merge file with column `"$2ndColumnName`" from file $secondCsvFileWithLocalPath" Import-Csv $outputCsvFileWithLocalPath | Select-Object $columnMergedFileBeforeInput, #{n=$2ndColumnName; e={ if ($hashFirstFileTwoColumns.ContainsKey($_.$FirstFileFirstColumnTitle)) { $hashFirstFileTwoColumns[$_.$FirstFileFirstColumnTitle] } Else { 'Not Found' }}}, * | Export-Csv "$outputCsvFileWithLocalPath-temp" -NoType -Force Move-Item "$outputCsvFileWithLocalPath-temp" $outputCsvFileWithLocalPath -Force Write-Host "Complete." Write-Host "" } This function will be called in a for loop for each column found in the first file (can contain an indefinite number). For testing, I am only using 2 columns from the first file. I'm getting an error output resulting the following: Select : Property cannot be processed because property "firstName" already exists. At C:\Scripts\Tests\Compare2CsvFilesOutput1WithMatchesOnly.ps1:490 char:43 + Import-Csv $outputCsvFileWithLocalPath | Select $columnMergedFileBeforeInput, # ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (#{firstName=L...ntName=asdfas}:PSObject) [Select-Object], PSArgume ntException + FullyQualifiedErrorId : AlreadyExistingUserSpecifiedPropertyNoExpand,Microsoft.PowerShell.Commands.SelectObjectC ommand I know the issue is where it says Select-Object $columnMergedFileBeforeInput,. How can I get the loop statement to insert the column in between the before column (name is specified), and append the rest using *? Update Just an fyi, changing this line Select-Object $columnMergedFileBeforeInput, #{n=$2ndColumnName..... to this line Select-Object #{n=$2ndColumnName..... works, it just attaches the columns out of order. That is why I'm trying to insert the column in between. Maybe if i do it this way but insert the columns in backwards using the for loop, this would work...
Not sure if this is the most efficient way to do it, but it should do the trick. It just adds the property to the record from file2, then reorders the output so secondName is the second column. You can output results to csv where required too (ConvertTo-Csv). $file1 = Import-Csv -Path file1.csv $file2 = Import-Csv -Path file2.csv $results = #() ForEach ($record In $file2) { Add-Member -InputObject $record -MemberType NoteProperty -Name secondName -Value $($file1 | ? { $_.firstName -eq $record.firstName } | Select -ExpandProperty secondName) $results += $record } $results | Select-Object -Property firstName,secondName,fileSplitter,Csv2ColumnOne,Csv2ColumnTwo,Csv2ColumnThree
I've created the following function. What it does is find the match (in this case "firstname") and adds the matching columnname to the new array afther the columnname on which the match is made (little difficult to explain in my poor English). function Add-ColumnAfterMatchingColumn{ [CmdletBinding()] param( [string]$MainFile, [string]$MatchingFile, [string]$MatchColumnName, [string]$MatchingColumnName ) # Import data from two files $file1 = Import-Csv -Path $MainFile $file2 = Import-Csv -Path $MatchingFile # Find column names and order them $columnnames = $file2 | gm | where {$_.MemberType -like "NoteProperty"} | Select Name | %{$_.Name} [array]::Reverse($columnnames) # Find $MatchColumnName index and put the $MatchingColumnName after it $MatchColumnNameIndex = [array]::IndexOf($columnnames, $MatchColumnName) if($MatchColumnNameIndex -eq -1){ $MatchColumnNameIndex = 0 } $columnnames = $columnnames[0..$MatchColumnNameIndex] + $MatchingColumnName + $columnnames[($MatchColumnNameIndex+1)..($columnnames.Length -1)] $returnObject = #() foreach ($item in $file2){ # Find corresponding value MatchingColumnName in $file1 and add it to the current item $item | Add-Member -Name "$MatchingColumnName" -Value ($file1 | ?{$_."$($MatchColumnName)" -eq $item."$($MatchColumnName)"})."$MatchingColumnName" -MemberType NoteProperty # Add current item to the returnObject array, in the correct order $newItem = New-Object psobject foreach ($columnname in [string[]]$columnnames){ $newItem | Add-Member -Name $columnname -Value $item."$columnname" -MemberType NoteProperty } $returnObject += $newItem } return $returnObject } When you run this function you will have the following output: Add-ColumnAfterMatchingColumn -MainFile C:\Temp\file1.csv -MatchingFile C:\Temp\file2.csv -MatchColumnName "firstname" -MatchingColumnName "secondname" | ft firstName secondname fileSplitter Csv2ColumnTwo Csv2ColumnThree Csv2ColumnOne --------- ---------- ------------ ------------- --------------- ------------- 1234 Value1 abc Value1 1234 1234 Value1 asd Value1 1234 3456 Value1 qwe Value1 3456 4567 Value3 mnb Value1 4567
How do you export objects with a varying amount of properties?
Warning - I've asked a similar question in the past but this is slightly different. tl;dr; I want to export objects which have a varying number of properties. eg; object 1 may have 3 IP address and 2 NICs but object 2 has 7 IP addresses and 4 NICs (but not limited to this amount - it could be N properties). I can happily capture and build objects that contain all the information I require. If I simply output my array to the console each object is shown with all its properties. If I want to out-file or export-csv I start hitting a problem surrounding the headings. Previously JPBlanc recommended sorting the objects based on the amount of properties - ie, the object with the most properties would come first and hence the headings for the most amount of properties would be output. Say I have built an object of servers which has varying properties based on IP addresses and NIC cards. For example; ServerName: Mordor IP1: 10.0.0.1 IP2: 10.0.0.2 NIC1: VMXNET NIC2: Broadcom ServerName: Rivendell IP1: 10.1.1.1 IP2: 10.1.1.2 IP3: 10.1.1.3 IP4: 10.1.1.4 NIC1: VMXNET Initially, if you were to export-csv an array of these objects the headers would be built upon the first object (aka, you would only get ServerName, IP1, IP2, NIC1 and NIC2) meaning for the second object you would lose any subsequent IPs (eg IP3 and IP4). To correct this, before an export I sort based on the number of IP properties - tada - the first object now has the most IPs in the array and hence none of the subsequent objects IPs are lost. The downside is when you then have a second varying property - eg NICs. Once my sort is complete based on IP we then have the headings ServerName, IP1 - IP4 and NIC1. This means the subsequent object property of NIC2 is lost. Is there a scalable way to ensure that you aren't losing data when exporting objects like this?
Try: $o1 = New-Object psobject -Property #{ ServerName="Mordor" IP1="10.0.0.1" IP2="10.0.0.2" NIC1="VMXNET" NIC2="Broadcom" } $o2 = New-Object psobject -Property #{ ServerName="Rivendell" IP1="10.1.1.1" IP2="10.1.1.2" IP3="10.1.1.3" IP4="10.1.1.4" NIC1="VMXNET" } $arr = #() $arr += $o1 $arr += $o2 #Creating output $prop = $arr | % { Get-Member -InputObject $_ -MemberType NoteProperty | Select -ExpandProperty Name } | Select -Unique | Sort-Object $headers = #("ServerName") $headers += $prop -notlike "ServerName" $arr | ft -Property $headers Output: ServerName IP1 IP2 IP3 IP4 NIC1 NIC2 ---------- --- --- --- --- ---- ---- Mordor 10.0.0.1 10.0.0.2 VMXNET Broadcom Rivendell 10.1.1.1 10.1.1.2 10.1.1.3 10.1.1.4 VMXNET If you know the types(NICS, IPS..), but not the count(ex. how many NICS) you could try: #Creating output $headers = $arr | % { Get-Member -InputObject $_ -MemberType NoteProperty | Select -ExpandProperty Name } | Select -Unique $ipcount = ($headers -like "IP*").Count $niccount = ($headers -like "NIC*").Count $format = #("ServerName") for ($i = 1; $i -le $ipcount; $i++) { $format += "IP$i" } for ($i = 1; $i -le $niccount; $i++) { $format += "NIC$i" } $arr | ft -Property $format
What about getting a list of all unique property headers and then doing a select on all the objects? When you do a select on an object for a nonexistent property it will create a blank one. $allHeaders = $arrayOfObjects | % { Get-Member -inputobject $_ -membertype noteproperty | Select -expand Name } | Select -unique $arrayOfObjects | Select $allHeaders Granted you are looping through ever object to get the headers, so for a very large amount of objects it may take awhile.
Here's my attempt at a solution. I'm very tired now so hopefully it makes sense. Basically I'm calculating the largest amount of NIC and IP note properties, creating a place holder object that has those amounts of properties, adding it as the first item in a CSV, and then removing it from the CSV. # Create example objects $o1 = New-Object psobject -Property #{ ServerName="Mordor" IP1="10.0.0.1" IP2="10.0.0.2" NIC1="VMXNET" NIC2="Broadcom" } $o2 = New-Object psobject -Property #{ ServerName="Rivendell" IP1="10.1.1.1" IP2="10.1.1.2" IP3="10.1.1.3" IP4="10.1.1.4" NIC1="VMXNET" } # Add to an array $servers = #($o1, $o2) # Calculate how many IP and NIC properties there are $IPColSize = ($servers | Select IP* | %{($_ | gm -MemberType NoteProperty).Count} | Sort-Object -Descending)[0] $NICColSize = ($servers | Select NIC* | %{($_ | gm -MemberType NoteProperty).Count} | Sort-Object -Descending)[0] # Build a place holder object that will contain enough properties to cover all of the objects in the array. $cmd = '$placeholder = "" | Select ServerName, {0}, {1}' -f (#(1..$IPColSize | %{"IP$_"}) -join ", "), (#(1..$NICColSize | %{"NIC$_"}) -join ", ") Invoke-Expression $cmd # Convert to CSV and remove the placeholder $csv = $placeholder,$servers | %{$_ | Select *} | ConvertTo-Csv -NoTypeInformation $csv | Select -First 1 -Last ($csv.Count-2) | ConvertFrom-Csv | Export-Csv Solution.csv -NoTypeInformation