Using pipelining to split a string into a multidimensional array

Consider the following string:

$foo = @"
cfa7b63c88ed1eb7443daeb12128084f17e1ac80
85e59563f059ecf45c104bef1eac9d70c22150ed
e207411bdb392a2d99719c221c8dd59e0dbebe26
df61cb22643321198656bfa7061c4e415eefdfef
3611ed35610793e814c8aa25715aa582ec08a8b6
089dfe7ceb9a0845342a9637527de65245ba297f qwerty
17570cc9387755367db0fc1c5c5f4757db7fd9b3 asdfgh
82a1be2b77e949cb45581c4d25bf962f77041846 uiop
0b726925f60c17795d4655f8ee37d51a3de70b87 lkjjh
7ed66867332bf06486117189701278cdabd31da6 zxcv
"@

I want to split it into an array of arrays such that the output looks like this:

[0] => [ "089dfe7ceb9a0845342a9637527de65245ba297f", "qwerty" ]
...
[4] => [ "7ed66867332bf06486117189701278cdabd31da6", "zxcv" ]

In other words, an array of 5 elements, with each element being a 2-element array. Note that lines missing the second part (i.e. nothing after the hash) must be excluded.

So far I've got this:

$bar = $foo -split "`r?`n" |?{ $_.length -gt 40 } |%{ $_.split(" ") }

But that results in a single-dimensional array of 10 elements.

Playing around a bit more yields:

$bar = $foo -split "`r?`n" |?{ $_.length -gt 40 } |select { $_.split(" ") }

which gives me an array of 5, but the items in that array are PSCustomObjects with a NoteProperty called $_.split(" ") that is itself an array of 2 strings. So close, yet so far - if I could just flatten out the NoteProperty I'd be in business!

Is it possible to achieve what I desire, bearing in mind these constraints:

If this is impossible, or there is something I'm fundamentally misunderstanding about how Powershell and its pipelines work (which I very strongly suspect to be the case), please educate me!

Upvotes: 4

Views: 619

Answers (2)

iRon
iRon

Reputation: 23663

If you want to put an array in the pipeline and prevent PowerShell to drop them as separate pipeline items, you will need to force it to a .Net array list by putting a comma in front of the array object:

PS C:\> $bar = $foo -split "`r?`n" | %{ ,$_.split(" ", 2) }
PS C:\> $bar[7]
82a1be2b77e949cb45581c4d25bf962f77041846
uiop
PS C:\> $bar[7][1]
uiop

Upvotes: 5

boxdog
boxdog

Reputation: 8432

This is a bit clunky, but I think it meets your requirements:

$foo = @"
cfa7b63c88ed1eb7443daeb12128084f17e1ac80
85e59563f059ecf45c104bef1eac9d70c22150ed
e207411bdb392a2d99719c221c8dd59e0dbebe26
df61cb22643321198656bfa7061c4e415eefdfef
3611ed35610793e814c8aa25715aa582ec08a8b6
089dfe7ceb9a0845342a9637527de65245ba297f qwerty
17570cc9387755367db0fc1c5c5f4757db7fd9b3 asdfgh
82a1be2b77e949cb45581c4d25bf962f77041846 uiop
0b726925f60c17795d4655f8ee37d51a3de70b87 lkjjh
7ed66867332bf06486117189701278cdabd31da6 zxcv
"@

$foo | ForEach-Object {$array = New-Object System.Collections.ArrayList}{
    $match = [Regex]::Match($foo, "(?<code1>\w+) (?<code2>\w+)")

    while($match.Success)
    {
        $array.Add(@($match.Groups["code1"].Value, $match.Groups["code2"].Value)) | Out-Null

        $match = $match.NextMatch()
    }
}{$array}

The output is an array of arrays (each with a matching line from $foo), and you can tag a pipeline onto the last bracket and continue processing.

Upvotes: 1

Related Questions