1

This powershell script takes up to 8 rows in a csv file and combines them into one row by duplicating the columns, and then saving in a result file (the first result file saved fine). If there are 16 rows in the csv it is meant to save a second result file etc.

e.g. in rows.csv:

first_field second_field third_field fourth_field

ball bat racket club

orange banana mango pear

In result1.csv:

first1 second1 third1 fourth1 first2 second2 third2 fourth2

ball bat racket club orange banana mango pear

I get an error:

New-Object : Cannot convert 'System.Object[]' to the type 'System.Collections.IDictionary' required by parameter 'Property'. Specified method is not supported.
At C:csv.ps1:19 char:42
+ $results = New-Object PSObject -Property $details
+                                          ~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [New-Object], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.NewObjectCommand

Note that the first new-object creation worked ok on line 16. In Powershell ISE if I rerun the script it errors on line 16 too. I have no idea what is wrong here but assume I need to destroy the PSObject after saving each csv file?

$csvObjects = import-csv C:\rows.csv
$results = @()
$counter=1
foreach ($item in $csvObjects){
    $detailsnew = [ordered] @{            
        "first$counter" = $item.'First_field'          
        "second$counter" = $item.'Second_field'           
        "third$counter"  = $item.'Third_field'
        "fourth$counter" = $item.'Fourth_field'
    }   
    $details  +=  $detailsnew 
    # modulus comparison returns remainder - write out file every 8
    if ($counter % 8 -eq 0) {
        if ($counter -eq 8) {
            #works on line below on first run but fails on subsequent runs within Powershell ISE
            $results = New-Object PSObject -Property $details 
        }

        if ($counter -eq 16) {
            # fails on line below
            $results2 = New-Object PSObject -Property $details
        }

        $quotient = $counter / 8
        $results | export-csv -Path c:\result"$quotient".csv -noType
        $details = @()
        $results = @()
    }                     
    $counter++           
}
#write out final file if number not divisible by 8
if (-not($counter % 8 -eq 0)) {
    $results += New-Object PSObjectF -Property $details
    $modulo = $counter % 8
    $quotient_plus1 = (($counter-$modulo) / 8) +1
    $results | export-csv -Path C:\result"$quotient_plus1".csv -noType 
}
ArcSet
  • 6,518
  • 1
  • 20
  • 34
dapa
  • 13
  • 2
  • The issue is you are taking a [Ordered] list and trying to convert it to a PSObject. – ArcSet Apr 13 '20 at 14:06
  • @ArcSet I tried removing [ordered] from the script but got the same error. I originally added [ordered] to improve readability of the csv file by keeping similar fields together, thanks – dapa Apr 13 '20 at 14:19

1 Answers1

4

tl;dr

  • $details = @() should be $details = [ordered] @{}

  • To also make repeated executions from the ISE work as intended, place another $details = [ordered] @{} statement before the loop.


  • In the very first loop iteration, $details is initially undefined, and $details += $detailsnew assigns the $detailsnew (ordered) hash table (an [ordered] @{ ... } hash table in PowerShell is of type System.Collections.Specialized.OrderedDictionary) as-is to $details (that is, with $details undefined, += effectively behaves the same as =).

  • The $detailsnew hash tables created in the next 7 iterations are then merged by += into the hash table already stored in $details.

  • After the 8th iteration, you mistakenly (re-)initialize $details as an array (@()), which is the source of the problem: subsequent use of += then adds a new array element to $details instead of merging the hash table entries, and passing an array rather than a hash table (or any dictionary type that implements System.Collections.IDictionary) to New-Object's -Property parameter then - predictably - fails.

    • Instead, (re)-initialize $details as an ordered hash table: $details = [ordered] @{}

In Powershell ISE if I rerun the script it errors on line 16 too.

The PowerShell ISE dot-sources the scripts it runs, which means that variables from previous invocations can linger.

As an aside: This equally applies to Visual Studio Code with its PowerShell extension, which you should consider migrating to[1]. However, there you can opt to create a new, temporary session instead, as described in the bottom section of this answer.

In your case, this means that $details already is an array in the very first iteration when you re-execute the script in the ISE.


[1] The PowerShell ISE is no longer actively developed and there are reasons not to use it (bottom section).

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • My pleasure, @dapa; good point about the connection between (ordered) hash tables and `IDictionary` not being obvious; I've update the answer to better reflect that – mklement0 Apr 13 '20 at 15:24