1

I'm having a problem with unexpected order of things being run and returned in the below Powershell script.

The Write-ArrayToTable function is to output the data in the arrays in a pretty table-like fashion via custom object.

The problem is when I call my Write-ArrayToTable function, it then does not return the data until AFTER the Read-Host command returns.

Below is the output from a run of the script, and the code itself is below that. The table output should be displayed BEFORE the Read-Host call but is instead held until the end and then displayed.

What am I missing? Any help greatly appreciated!

Output:

Test: y
Label1 Label2
------ ------
Test1  Test3
Test2  Test4
y

Code:

Function Write-ArrayToTable{
  param(
      [String[]]$Names,
      [Object[][]]$Data
  )
  for($i = 0;; ++$i){
    $Props = [ordered]@{}
    for($j = 0; $j -lt $Data.Length; ++$j){
      if($i -lt $Data[$j].Length){
        $Props.Add($Names[$j], $Data[$j][$i])
      }
    }
    if(!$Props.get_Count()){
      break
    }
    [PSCustomObject]$Props
  }
}

$arr1 = @("Test1","Test2")
$arr2 = @("Test3","Test4")

Write-ArrayToTable "Label1","Label2" $arr1,$arr2

Read-Host "Test"
xeric080
  • 99
  • 8
  • 2
    Probably the same root cause as explained in this answer by @mklement0… https://stackoverflow.com/a/59331305/3156906 - ```Format-Table``` can delay for up to 300ms before rendering output, by which time ```Read-Host``` is already blocking while waiting for a response from the user so you don’t see anything until ```Read-Host``` finishes. – mclayton Oct 28 '22 at 17:16
  • 2
    As an aside, there’s no guarantee your code will generate tabular output - PowerShell has some rules that determine whether the default behaviour for output formatting uses ```format-list``` or ```format-table``` - your test cases probably all just happen to select ```format-table```, but if you try with a single property you might see it use ```format-list``` instead… – mclayton Oct 28 '22 at 17:21
  • @mclayton Thank you for that information. Any idea of a better way to get my desired output result of side by side tabular display of arrays? Not asking for code, just a general direction to look into. – xeric080 Oct 28 '22 at 17:29
  • 1
    If you rename your function something like ```Select-From2DArray```, which is more realistically what it does, then you can just pipe the output from that into ```Format-Table``` and it will always output as tabular data. – mclayton Oct 28 '22 at 17:34
  • Awesome, I've got it working thanks to you and @stackprotector. Greatly appreciate the info on what's happening under the hood. Helping me learn more! – xeric080 Oct 28 '22 at 17:41
  • 1
    Although I just noticed it’s a *jagged* array, not a *2D* array, so maybe ```Select-FromJaggedArray``` would be a better name… – mclayton Oct 28 '22 at 17:41
  • 1
    To add to mclayton's comments: The lack of synchronization between pipeline output and to-host output (as well as other output streams) is limited to PS v5+ and a very specific - albeit still common - scenario: implicitly _table_-formatted output for types that do _not_ have formatting data defined for them. The - suboptimal - workaround is to force the pipeline output synchronously to the host (display) with `Out-Host`. See the linked duplicate for details. – mklement0 Oct 28 '22 at 17:49

1 Answers1

1

Instead of dropping you objects like this:

[PSCustomObject]$Props

You can be more explicit:

$Props | Out-String

If you want to print all objects in one table, collect them first, before printing:

Function Write-ArrayToTable{
  param(
      [String[]]$Names,
      [Object[][]]$Data
  )
  $myProps = for($i = 0;; ++$i){
    $Props = [ordered]@{}
    for($j = 0; $j -lt $Data.Length; ++$j){
      if($i -lt $Data[$j].Length){
        $Props.Add($Names[$j], $Data[$j][$i])
      }
    }
    if(!$Props.get_Count()){
      break
    }
    [PSCustomObject]$Props
  }
  $myProps | Format-Table
}
stackprotector
  • 10,498
  • 4
  • 35
  • 64
  • 2
    Since the problem is purely a _display_ problem, it's better to tackle it at that level instead of rewriting what _data_ a function outputs: `Write-ArrayToTable "Label1","Label2" $arr1,$arr2 | Out-Host` will do. This forces _synchronous_ display output and thereby resolves the original problem, which is the _asynchronous_ display output you get with _implicitly_ table-formatted output, causing the `Read-Host` prompt to unexpectedly display first. – mklement0 Oct 28 '22 at 17:56