2

I am trying to split an array read off a csv file and I cannot capture anything but the first array element. Here is my code

$EmployeeLists = @()
$ManagerLists = @()

$CSVFiles = Import-CSV "C:\T2\SetManagers\EmployeeManager.csv"

ForEach($CSVFile in $CSVFiles) { $EmployeeLists += ($CSVFile.Employee) }
ForEach($CSVFile in $CSVFiles) { $ManagerLists += ($CSVFile.Manager) }

ForEach($EmployeeList in $EmployeeLists) { $EmployeeLists.Split(",")[0] | Out-File "C:\T2\SetManagers\ESplit.txt" -Append }
ForEach($ManagerList in $ManagerLists) { $ManagerLists.Split(",")[0] | Out-File "C:\T2\SetManagers\MSplit.txt" -Append }

My put put looks like this

Smith
Smith
Smith
Smith
Smith
Smith
Smith
JRN
  • 269
  • 1
  • 3
  • 19

2 Answers2

3

Maximilian Burszley's helpful answer explains the problem with your code and offers an effective solution.

  • If you're happy with your (fixed) code's performance and you see no need to improve your code, that's all you need.

  • To learn about reusable techniques that both shorten and speed up your code, read on.


A concise, PowerShell-idiomatic solution that performs much better (PSv4+):

# Read the CSV rows (into custom objects whose properties contain the
# column values).
$rows = Import-CSV "C:\T2\SetManagers\EmployeeManager.csv"

# Collect all Employee and Manager column values in an array each.
$employeeLists = $rows.Employee
$managerLists = $rows.Manager

# Loop over all column values, extract only the first ","-separated token each
# and send the combined output to an output file.
$employeeLists.ForEach({ ($_ -split ',')[0] }) > "C:\T2\SetManagers\ESplit.txt"
$managerLists.ForEach({ ($_ -split ',')[0] }) >  "C:\T2\SetManagers\MSplit.txt"

Specifically, the code above avoids:

  • Building up an array in a loop with +=, which necessitates recreating the array (with the new value appended) in each iteration.

    • Instead, it uses member-access enumeration (PSv3+) to directly retrieve an array of property values (e.g., $employeeLists = $rows.Employee)

    • Even in PSv2 a relatively concise and more efficient form is possible; the PSv2 equivalent of $employeeLists = $rows.Employee is:

          # *PowerShell* does the work of collecting the outputs from the individual
          # loop iterations and simply returns an array.
          $employeeLists = foreach ($row in $rows) { $row.Employee }
      
    • Finally, if you do need to build up a collection iteratively and speed matters, use an extensible collection type such as [System.Collections.Generic.List[object]] and its .Add() method instead of arrays with +=.

  • Calling Out-File in a loop, which incurs the cmdlet's startup and teardown cost in each iteration and also requires reopening and closing the file every time.

    • Instead, the combined output of the statement is written to the output file in a single Out-File call (shortened to > for brevity).
  • The PSv4+ .ForEach() method rather than a foreach loop performs better (though only slightly), and has the advantage that you can directly use it as the first segment of a pipeline (whereas a foreach loop would require wrapping in $(...)).
    In PSv3-, use a foreach loop.

mklement0
  • 382,024
  • 64
  • 607
  • 775
2

Proper formatting goes a long way:

$csv = Import-Csv -Path C:\T2\SetManagers\EmployeeManager.csv

foreach ($list in $csv) {
    $list.Employee.Split(',')[0] | Out-File -Path C:\T2\SetManagers\ESplit.txt -Append
    $list.Manager.Split(',')[0] | Out-File -Path C:\T2\SetManagers\MSplit.txt -Append
}

Your problem was referring to the overall list instead of a single element in your foreach loops.

Maximilian Burszley
  • 18,243
  • 4
  • 34
  • 63