0

Let's say I want to read some strings from file, and for each string I want to split it. In the end, I want to get array of arrays of strings. Here is how I try to do it:

$toArchive = "./folders.txt"
$folderList = Get-Content $toArchive | ForEach-Object { @($_.split(" ")) }

Get-Content should return an array of strings, and for each of those strings, I expect to get another array of strings. But instead of that my folderList is Object[], as if it was flattened. For example, if my input file looks like this:

Docs /home/keddad/Documents/
NotDocs /home/keddad/NotDocuments/

I get array of 4 elements:

Docs                                                                                                                                                                                                                                                                           
/home/keddad/Documents/                                                                                                                                                                                                                                                        
NotDocs                                                                                                                                                                                                                                                                        
/home/keddad/NotDocuments/

What am I doing wrong here?

Update: just to clarify, it is definitely array of 4 strings:

PS /home/keddad/Documents/tbackup> $toArchive = "./folders.txt"
PS /home/keddad/Documents/tbackup> $folderList = Get-Content $toArchive | ForEach-Object { @($_.split(" ")) }
PS /home/keddad/Documents/tbackup> $folderList.count
4
PS /home/keddad/Documents/tbackup> $folderList
Docs
/home/keddad/Documents/
NotDocs
/home/keddad/NotDocuments/
keddad
  • 1,398
  • 3
  • 14
  • 35
  • 1
    What does $folderlist.count show? – Doug Maurer Apr 03 '21 at 18:57
  • @DougMaurer 4, I'll add it to the question – keddad Apr 03 '21 at 19:11
  • 3
    Powershell loves to “help” users by unrolling collections. Should be able to solve by adding a comma in front of your split. May not even need the parenthesis. ,$_.Split(“ “) or ,($_.Split(“ “) – Doug Maurer Apr 03 '21 at 19:22
  • 2
    I don't like the comma-operator, IMO it makes the code cryptic. An alternative could be to put the array in a `[PSCustomObject]` like this: `ForEach-Object{ [PSCustomObject]@{ value = $_.split(" ") } }`. PowerShell will never flatten a `[PSCustomObject]`. – zett42 Apr 03 '21 at 19:34
  • 1
    @zett42, there is a verbose, conceptually clear alternative, but it adds overhead: Instead of `, 'foo bar'.Split(" ")` you can use `Write-Output -NoEnumerate 'foo bar'.Split(" ")`. – mklement0 Apr 03 '21 at 21:04
  • 2
    @biryulin04, note that `@(...)` isn't an array _constructor_, but a _guarantor_, so to speak: It collects a command's output or an expression's _enumerated_ output in an - always _new_ - `[object[]]` _array_ - even if that output is a _single_ object. Therefore, something like `@(@(1, 2)` does _not_ create a _nested_ array; it is effectively (but inefficiently) the same as `@(1, 2)`; to construct a nested array, use the unary form of `,`: `, (1, 2)` - see [this answer](https://stackoverflow.com/a/45091504/45375). – mklement0 Apr 03 '21 at 21:07
  • Does this answer your question? [Why does PowerShell flatten arrays automatically?](https://stackoverflow.com/questions/57023626/why-does-powershell-flatten-arrays-automatically) – zett42 Apr 05 '21 at 14:36

2 Answers2

4

You're missing the array construction operator ,

(Get-Content .\folders.txt | ForEach-Object ({ ,$_.split(" ") }))

Which will render

> (Get-Content .\folders.txt | ForEach-Object ({ ,$_.split(" ") })).count
2

and, I believe, the structure you like, for example:

> (Get-Content .\folders.txt | ForEach-Object ({ ,$_.split(" ") }))[0]
Docs
/home/keddad/Documents/

> (Get-Content .\folders.txt | ForEach-Object ({ ,$_.split(" ") }))[0][0]
Docs

> (Get-Content .\folders.txt | ForEach-Object ({ ,$_.split(" ") }))[0][1]
/home/keddad/Documents/

Or visualized holistically as JSON:

> (Get-Content .\folders.txt | ForEach-Object ({ ,$_.split(" ") })) | ConvertTo-Json

[
    {
        "value":  [
                      "Docs",
                      "/home/keddad/Documents/"
                  ],
        "Count":  2
    },
    {
        "value":  [
                      "NotDocs",
                      "/home/keddad/NotDocuments/"
                  ],
        "Count":  2
    }
]
msanford
  • 11,803
  • 11
  • 66
  • 93