miércoles, 15 de mayo de 2013

PowerShell: Búsqueda Binaria en arrays de objetos personalizados

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

No hay comentarios:

Publicar un comentario