El problema del IComparer
Siguiendo con el tema de la búsqueda rápida (haciendo
búsqueda binaria), en éste artículo ya había encontrado
cómo hacer una búsqueda binaria sobre un ArrayList desde Power Shell, pero al complicar un poco el tema, usando arreglos de objetos propios encontré un problema...
El problema es que al ser objetos propios debemos indicarle sobre qué campo hacer la búsqueda y cómo se ordenan los objetos... El mismo problema tenemos al ordenar (hacer un sort) sobre un Array o ArrayList.
Veamos a un ejemplo, tenemos un ArrayList de objetos Empleado, con atributos Nombre, Apellido, Legajo... por ej uso esta funcion para crearlos:
function Nuevo-Empleado(){
param ($Nombre, $Apellido, $Codigo)
$empleado= New-Object PSObject
$empleado| Add-Member -type NoteProperty -Name Nombre -Value $Nombre
$empleado| Add-Member -type NoteProperty -Name Apellido -Value $Apellido
$empleado| Add-Member -type NoteProperty -Name Legajo -Value $Codigo
return $empleado
}
Y supongamos que los cargamos de un archivo csv desordenado y queremos buscar por nro de legajo...
[System.Collections.ArrayList] $listaEmpleados = New-Object System.Collections.ArrayList
$listaEmpleados = Import-Csv -h Nombre, Apellido, Codigo
$listaEmpleados = $listaEmpleados | sort Codigo # ordeno por codigo
Ahora el tema es.. cómo hace para ordenar por código... bueno, supongamos que código es string... luego, para poder hacer el BinarySearch...
La función de comparación
$src = @'
using System;
using System.Collections;
using System.Management.Automation;
public sealed class ScriptBlockComparer : IComparer
{
ScriptBlock _Comparer;
public ScriptBlockComparer(ScriptBlock Comparer) { ComparerScript = Comparer; }
public ScriptBlock ComparerScript
{
get { return _Comparer; }
set { _Comparer = value; }
}
public int Compare(object x, object y)
{
try {
return (int)ComparerScript.Invoke(x, y)[0].BaseObject;
} catch(Exception ex) {
throw new InvalidOperationException("Comparer Script failed to return an integer!", ex);
}
}
}
'@
Add-Type -TypeDefinition $src -Language CSharpVersion3
$cmp = New-Object ScriptBlockComparer {
Param($primero, $segundo)
$pos = $primero.Codigo.CompareTo($segundo)
return $pos
}
.......
..........
mas codigo
...........
... y...
$pos = $list.BinarySearch($codigoABuscar, $cmp)
Y nos devuelve en pos la posición del elemento buscado en el array (empezando desde 0) y un nro <0 si no se encontró. Espero les sirva de ayuda!
Nota: si les tira error con la version de .Net pueden intentar cambiar
Add-Type -TypeDefinition $src -Language CSharpVersion3
Por:
Add-Type -TypeDefinition $src -Language CSharp
Ya que espera tener instalado el Framework .Net versión 3.5