0

I read a CSV file into an array of objects (using Import-Csv). If I write that data back to disk with Export-Csv, I get exactly what I expected, a well formatted CSV.

Now I need to change some data in that CSV. One of them being strings that need to be replaced. But if I do a simple:

$thisIsTheArrayOfObjects -replace "Old ID", "New ID" 

it results in an array of STRINGS. (the replacement happened, but somehow all my objects in the array got casted to strings)

Of course now the Export-Csv does not deliver the expected result. It's just exporting a file with all the length of the strings in my array.

How can I replace strings in properties of PSOBJECTS in an array?

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
EKortz
  • 25
  • 7

1 Answers1

4

How can i replace strings in properties of PSOBJECTS in an array?

You do it for each object.

$thisIsTheArrayOfObjects | ForEach-Object {
    $_.foo = $_.foo -replace "Old ID" , "New ID" # replace string in a property
    $_                                           # output the object back onto the pipeline
}

Keep in mind that there are two gotchas with -replace:

  • It works with regex. Technically you must regex-escape the first parameter ([regex]::escape("Old ID")) if you want to do a literal replacement, unless "Old ID" does not contain any characters that are special in regex. If it comes from user input, it's always wise to escape it.
  • It's case-insensitive.

To do case-sensitive, literal replacements, use the .NET-native String.Replace(), which is also available:

$thisIsTheArrayOfObjects | ForEach-Object {
    $_.foo = $_.foo.Replace("Old ID", "New ID") # replace string in a property
    $_                                          # output the object back onto the pipeline
}

To apply an operation to every column in the CSV data, you have to create a nested loop. The Get-Member cmdlet can help. The principle stays the same as above.

Import-Csv "input.csv" -PipelineVariable row | ForEach-Object {
    $row | Get-Member -MemberType NoteProperty -PipelineVariable col | ForEach-Object {
        $colname = $col.Name
        $row.$colname = $row.$colname -replace "Old ID", "New ID"
        $row
    }
} # | Export-Csv "output.csv"

Note the use of pipeline variables to sidestep any $_ confusion inside the inner loop.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Thank you very much for the very quick reply. But there is one more issue. As I read the data from a csv, there are hundrets of properties per object and these might even change, depending on the csv i loaded. Therefore the property name "foo" in your example is not available... Do I iterate over all properties now? – EKortz Oct 21 '19 at 14:21
  • The problem is not enumerating the objects (PowerShell operators do that just fine), but that the replacement operation is applied to the entire object, not the value of one of its properties. Because of that the object is first converted to its string representation, then the replacement operation is applied to the resulting string. – Ansgar Wiechers Oct 21 '19 at 14:26
  • [This](https://stackoverflow.com/a/11885405/1630171) is probably a more elegant approach. – Ansgar Wiechers Oct 21 '19 at 17:18
  • Many ways to skin a cat. – Tomalak Oct 21 '19 at 17:33