Tengo dos matrices de objetos en mi script de powershell

El primero tiene valores como este

@{Id=1, Count=24}
@{Id=2, Count=34}

Segunda

@{Id=1, Name="Some name"}
@{Id=2, Name="Some other name"}

Me gusta unirme a estos dos así que consigo

@{Id=1, Count=24 Name="Some name"}
etc

Eventualmente necesito escribirlo en un archivo csv.

1
Jepzen 25 ago. 2020 a las 12:12

4 respuestas

La mejor respuesta

Si tu matriz tiene objetos como dices como

$array1 = [PsCustomObject]@{Id=1; Count=24}, 
          [PsCustomObject]@{Id=2; Count=34}
$array2 = [PsCustomObject]@{Id=1; Name="Some name"}, 
          [PsCustomObject]@{Id=2; Name="Some other name"}

Puedes hacer algo como esto:

# this updates the objects in $array1
foreach ($item in $array1) {
    foreach ($obj in $item) {
        $obj | Add-Member -MemberType NoteProperty -Name 'Name' -Value ($array2 | Where-Object { $_.Id -eq $obj.Id }).Name
    }
}

# show on screen
$array1

# export to CSV
$array1 | Export-Csv -Path 'D:\Test\Joined.Csv' -NoTypeInformation

Salida en pantalla:

Id Count Name           
-- ----- ----           
 1    24 Some name      
 2    34 Some other name

Si las matrices almacenan Hashtables como

$array1 = @{Id=1; Count=24}, 
          @{Id=2; Count=34}
$array2 = @{Id=1; Name="Some name"}, 
          @{Id=2; Name="Some other name"}

Entonces usa esto:

# this returns a new array of PSObjects
$result = foreach ($item in $array1) {
    foreach ($hash in $item) {
        $hash['Name'] = ($array2 | Where-Object { $_.Id -eq $hash.Id }).Name
    }
    [PsCustomObject]$hash
}

# show on screen
$result

# export to CSV
$result | Export-Csv -Path 'D:\Test\JoinedFromHashes.Csv' -NoTypeInformation

Salida en pantalla:

Name            Id Count
----            -- -----
Some name        1    24
Some other name  2    34

Tenga en cuenta que las tablas hash están desordenadas de forma predeterminada, por lo que el orden en el que aparecen las columnas puede variar

0
Theo 25 ago. 2020 a las 10:07
$first = $(
    @{Id=1; Count=24}
    @{Id=2; Count=34}
)
$second = $(
    @{Id=1; Name="Some name"}
    @{Id=2; Name="Some other name"}
)

$first,$second `
| %{ $_ } `
| &{
    Param ($key)
    Begin {
        $output = @{}
        
        Function Input {
            Param ($id)
            Begin {
                $merged = $output[$id]
                if (!$merged) {
                    $merged = [PSCustomObject]@{$key=$id}
                }
            }
            Process {
                if ($_.Name -ne $key) {
                    $merged `
                    |  Add-Member `
                        -Type NoteProperty `
                        -Name $_.Name `
                        -Value $_.Value
                }
            }
            End {
                $output[$id] = $merged
            }
        }
    }
    Process {
        $_.GetEnumerator() `
        | Input -id $_[$key]
    }
    End {
        $output.Values
    }
} -key 'Id' `
| ConvertTo-Csv

Salidas

Id Count Name
-- ----- ----
 2    34 Some other name
 1    24 Some name

Entonces

"Id","Count","Name"
"2","34","Some other name"
"1","24","Some name"
0
Hashbrown 25 ago. 2020 a las 10:09

Usando este Join-Object cmdlet (ver también: En Powershell, ¿cuál es la mejor manera de unir dos tablas en una? ) que tanto los objetos (personalizados) (incluidas las tablas de datos) como las tablas hash:

$Array1 = @{Id=1; Count=24},
          @{Id=2; Count=34}
    
$Array2 = @{Id=1; Name="Some name"},
          @{Id=2; Name="Some other name"}
$Array1 | Join $Array2 -On Id
Count Id Name
----- -- ----
   24  1 Some name
   34  2 Some other name
0
iRon 25 ago. 2020 a las 10:30

Para la posteridad de las personas que vienen de Google, aquí hay una forma idiomática de desglosar el problema.

$first = $(
    @{Id=1; Count=24}
    @{Id=2; Count=34}
)
$second = $(
    @{Id=1; Name="Some name"}
    @{Id=2; Name="Some other name"}
)

$first,$second `
| %{ $_ } `
| To-Object `
| Group-Object -Property Id `
| %{ $_.Group | To-Hash | Merge } `
| To-Object `
| ConvertTo-Csv

Si desea mantenerlos como tablas hash y aún no obtener el CSV, elimine las dos últimas líneas, o si eran objetos para empezar, elimine la primera To-Object.

Para lo cual necesitarías lo siguiente:

Filter To-Object {
    $output = [PSCustomObject]@{}
    $_.GetEnumerator() `
    | %{
        $output `
        |  Add-Member `
            -Type NoteProperty `
            -Name $_.Name `
            -Value $_.Value
    }
    $output
}

Filter To-Hash {
    $out = @{}
    $in = $_
    $in `
    | Get-Member -MemberType NoteProperty `
    | %{
        $out[$_.Name] = $in.($_.Name)
    }
    $out
}

Function Merge {
    Begin {
        $output = @{}
    }
    Process {
        $_.GetEnumerator() `
        | %{
            $output[$_.Name] = $_.Value
        }
    }
    End {
        $output
    }
}

No es tan eficiente como mi otra respuesta (construida para un propósito específico), pero serán mucho más útiles para cosas fuera del caso de uso exacto del autor de la pregunta, y mucho mejor para que cualquiera que esté aprendiendo PowerShell pueda echar un vistazo :)

0
Hashbrown 25 ago. 2020 a las 10:45