Comparaciones de matrices facilitadas con PowerShell

Usando un poco de PowerShell kung-fu, puedes comparar fácilmente arreglos de objetos de todo tipo. Hay muchos escenarios diferentes en los que te puedes encontrar, así que profundicemos y veamos las formas en que podemos construir PowerShell para comparar arreglos.

Para determinar la mejor manera de comparar arreglos, primero debes averiguar qué tipos de elementos hay en ambos arreglos.

  • ¿Ambos arreglos contienen el mismo tipo de objeto?
  • ¿Ambos arreglos tienen el mismo número de elementos?
  • ¿Hay diferentes tipos de objetos en cada arreglo?

Debes conocer la respuesta a cada una de estas preguntas antes de poder comparar arreglos con precisión. Cubramos cada escenario.

Comparando arreglos de cadenas

Una de las formas más sencillas de comparar arreglos con PowerShell es si tienes dos arreglos que solo contienen cadenas. Cuando te encuentres en esta posición, tienes algunas formas diferentes de comparar cadenas en los arreglos.

Usando los operadores -Contains o -In

El operador -contains es un operador de PowerShell que te permite verificar si un objeto está en una colección. El operador -contains no entiende nativamente las colecciones, pero puedes construir código para que haga lo que necesitas.

Supongamos que una colección (arreglo) contiene cuatro cadenas como se muestra a continuación.

$array = @('blue','red','purple','pink')

El operador -contains funciona verificando si una sola cadena está en ese arreglo de la siguiente manera:

$array -contains 'pink'

Cuando la colección a la izquierda contiene esa cadena, PowerShell devolverá Verdadero. Si no, devolverá Falso.

PowerShell -contains Operator

Podemos comparar arrays usando el operador -contains leyendo cada cadena en un array y comprobando si el otro array contiene esa cadena.

Digamos que quiero comparar dos arrays para ver qué cadenas del primer array existen en el segundo array.

$array = @('blue','red','purple','pink')
$array2 = @('brown','red','black','yellow')

$array | ForEach-Object {
    if ($array2 -contains $_) {
        Write-Host "`$array2 contains the `$array1 string [$_]"
    }
}

Alternativamente, podrías usar el operador -in, que es idéntico al operador -contains, pero la sintaxis es opuesta. Al usar el operador -contains, el array se define en el lado izquierdo. Al usar el operador -in, el array se define en el lado derecho como se muestra a continuación:

$array | ForEach-Object {
    if ($_ -in $array2) {
        Write-Host "`$array2 contains the `$array1 string [$_]"
    }
}

Usando Where-Object

Alternativamente, también podrías usar el cmdlet Where-Object para devolver todas las cadenas en un array en el otro como se muestra a continuación.

$array | Where-Object -FilterScript { $_ -in $array2 }

Usando el Cmdlet Compare-Object

También puedes usar PowerShell para comparar arrays usando el cmdlet Compare-Object. Este cmdlet toma un objeto de referencia y un objeto de diferencia y devuelve un indicador de lado que indica qué elementos están y no están en ninguno de los arrays.

Compare-Object -ReferenceObject $array -DifferenceObject $array2
Using Compare-Object

Puedes ver a continuación que el cmdlet Compare-Object te permite comparar ambos arrays a la vez. Si la propiedad SideIndicator es =>, esto significa que la propiedad InputObject devuelta está en el valor de DifferenceObject y no en el valor de ReferenceObject, y viceversa para el indicador de lado <=.

De forma predeterminada, Compare-Object devuelve las diferencias. También puedes devolver todas las cadenas en cada matriz que están en ambas utilizando el parámetro IncludeEqual.

Comparing arrays with Compare-Object

Comparando matrices de objetos complejos

Lo suficientemente sencillo, ¿verdad? Ahora, vamos a agregar objetos a la mezcla. Digamos que tenemos un campo en esta base de datos de recursos humanos y nos gustaría llenar esto en el campo de descripción del Active Directory. Antes de hacer esto, primero debemos tener un identificador común. En mi entorno, hay un número de empleado tanto en la base de datos de recursos humanos como en el atributo personalizado del Active Directory. Así que intentemos hacer coincidir esto.

Primero, veamos qué sucede si intentamos nuestro enfoque anterior. Aquí está el archivo CSV que estamos usando.

CSV output

Así es como obtengo nuestros dos conjuntos de datos.

$ad_users = Get-AdUser -Filter {enabled -eq $true} -Properties employeeNumber | select employeenumber,samaccountname,description
$users_from_database = Import-Csv 'database_users.csv' | select employee number

¿Qué sucede cuando ejecutamos estas dos matrices a través del mismo escenario que nuestras cadenas? Absolutamente nada. ¿Por qué?

La razón es porque normalmente no puedes decir $object1 -eq $object2 porque los objetos son más complejos que una simple cadena, booleano o entero. Hay algunas otras circunstancias en las que esto no es cierto, pero intento tener la costumbre de comparar las propiedades de los objetos; no los objetos completos. Así que en este caso, tenemos que hacer algo como esto:

$ad_user[0].employeeNumber -eq $users_from_database[0].employeeNumber

¿Cuál es la solución? Actualmente, tengo dos soluciones. Cuando se trata de miles de objetos, no es rápido pero funciona. Me gustaría saber si alguien más tiene alguna otra sugerencia.

$ad_employee_numbers = $ad_users | % {$_.employeenumber}

## Crea una matriz de cadenas solo con el número de empleado del usuario de AD
$users_from_database | Where-Object -FilterScript { $ad_employee_numbers -contains $_.employeeNumber }

Podemos usar también Compare-Object aunque esto es más lento.

$ad_employee_numbers = $ad_users | ForEach-Object {$_.employeenumber}

## Crear un array de cadenas solo del número de empleado del usuario de AD
$database_user_employee_numbers = $users_from_database | ForEach-Object {$_.employeenumber}

## Crear un array de cadenas solo del número de empleado del usuario de la base de datos
(Compare-Object $ad_employee_numbers $database_user_employee_numbers -IncludeEqual | Where-Object -FilterScript {$_.SideIndicator -eq '=='}).InputObject

Conclusión

Existen muchas formas diferentes de usar PowerShell para comparar arrays. Los arrays pueden ser complejos y entender completamente los objetos dentro de los arrays te ayudará enormemente a compararlos.

Source:
https://adamtheautomator.com/powershell-compare-arrays/