martes, 29 de diciembre de 2009

Buscar un elemento dentro de un SDT en GeneXus

En GeneXus, no hay una forma de buscar un elemento dentro de una colección (SDT), que no sea recorriendo toda la lista. Hay una función IndexOf, pero compara referencias y no el contenido de los elementos.

Ejemplo:
&esta = Boolean.False
for &depUbiItem in &depUbis
    if &depUbiItem.DepId = &DepId and &depUbiItem.CicUbiId = &CicUbiId
        &esta = Boolean.True
        exit
    endif
endfor

Esto no parece ser lo más elegante... No debería necesitar 7 líneas de código para saber si un elemento está en la colección, lo debería poder hacer en una sola línea.

El problema es que dado un SDT, GeneXus no tiene forma de saber cuales son los elementos que quiero comparar. En el ejemplo, estoy comparando DepId y CicUbiId, pero el SDT tiene varios campos más.

Una posibilidad sería poder definirle comportamiento a los SDTs, de forma de poder decirle cual es la función de comparación que tengo que usar. Esta función de comparación se definiría una vez para el SDT, y siempre que se quiera buscar un elemento se haría usando este comparador.

Otra opción que me gustaría más, es poder definir on-line la función de comparación. En C# por ejemplo, usando lambda expressions, quedaría algo así:
bool esta = depUbis.Exists(d => ((d.DepId = depId) && (d.CicUbiId = cicUbiId)));

O escribiendolo como me gustaría verlo en GeneXus:
&esta = &depUbis.Exists(d => d.DepId = &DepId and d.CicUbiId = &CicUbiId)
 Esto se lee "existe un elemento d en la colección &depUbis que cumple que d.DepId = &DepId y d.CicUbiId = &CicUbiId".

Las colecciones en C# definen otras funciones que también sería intereante tener, como ser:
  • First: devuelve el primer elemento que cumple con la condición
  • Where: devuelve otra colección con los elementos que cumplen con la condición
  • Select: permite hacer una proyección, devuelve otra colección pero de otro tipo

8 comentarios:

  1. Muy buena idea. :)

    Sin embargo no te parece mejor idea la de unificar la navegación de SDT's/Collections/Vectores con nuestro querido For Each? van mis sugerencias.

    1 - Que la navegación SDT sea igual que la navegación sobre Tablas = Unificación de lenguaje.
    Usar For Each para SDT's/Collections/Vec con soporte de todo el poder del lenguaje (Order, Where, When), que luego GX vea cómo lo expresa en lenguaje generado.

    For Each in &depUbis
    Where DepId = &DepId and CicUbiId = &CicUbiId
    &esta = Boolean.True
    Exit
    EndFor

    2 - Algo que desde hace tiempo hace falta, el Exists y el "TOP n Registros", aplicado al For Each

    &Exist = For Each Exists in &depUbis Where DepId = &DepId and CicUbiId = &CicUbiId

    Internamente el Exist vería si retorna por lo menos 1 elemento la colección o para el caso del DBMS si existe 1 registro por lo menos (TOP 1, pero ideal sería sin retornarlo)

    De la misma forma, para navegación entre Tablas que permita el mismo concepto y que a nivel de sentencias se optimizado para cada DBMS (Con top o consulta de existencia nativa)

    3 - Para el caso del TOP podría ser algo como.

    For Each TOP(10) in &depUbis
    Where DepId = &DepId and CicUbiId = &CicUbiId
    Order by (DepSize)
    Print TOP_10
    EndFor

    Para este caso internamente se ordenaría la colección con orden invertido y se tomarían solamente 10 registros.
    Si fuera para DBMS, el TOP debería ir contra sentencia nativa indicando al DBMS que solamente retorne la cantidada X de registros, de esta forma si tengo 1 millon de registros entre el dbms y el appserver solo viajarán 10 registros dejando al DBMS la tarea de filtrado.

    La implementación de FOR EACH FIRST se podría hacer igual al FOR EACH TOP(1)

    Con las extensiones al For Each y la inclusión de For Each para todo tipo de elemento navegable sería feliz :-)

    Lo evaluarán Artech para la EVO II o EVO III?

    ResponderEliminar
  2. David, están buenas tus sugerencias, también me sirven.

    No se si lo evaluarán, pero por lo menos el planteo está hecho :)

    ResponderEliminar
  3. Me anoto a la sugerencia de David.
    Con el for each y el where quedaria muy bueno!

    ResponderEliminar
  4. También me anoto a la sugerencia de hacer un for each de una colección de SDT con un where, estaría sinceramente interesante.

    ResponderEliminar
  5. Hola gente
    y ya que estamos, sumo algunas ideas

    1- quizá se le pueda agregar una PK al SDT o una especie de indice unique y nos solucionaría mas de un dolor de cabeza.

    2 - Sumada a (1) implemetar clausula When Duplicate al agregar un elemento al SDT y when none al usar el recorrerlo.

    3- Y creo que la tercera cae sola...ya es hora de poder hacer un break dentro de los SDT anidando un par de For each.

    con ésto mas las que ya aportó el resto, incrementaríamos la potencia un 100 x 100.

    Saludos

    ResponderEliminar
  6. Franco, buenas sugerencias!!

    Hace un rato charlaba sobre el tema con un compañero de trabajo (Marcos) y saltó justamente lo que mencionaste en el punto 1, la necesidad de tener algo como una PK o "KEY".

    Punto 2 y 3 estaría bárbaro.

    ResponderEliminar
  7. Hola David!, recuerdo haberlo sugerido hace unos años, con gx8 ni bien trabajé con los SDT. Nos hubiese resuelto entre otras cosas, evitar armar tablas 'temporales' cuando no podíamos resolver de forma natural el orden de algún reporte, pero la idea nunca prosperó y aun hoy veo que siguen siendo buenas sugerencias.
    Casualmente hoy necesito armar bandejas de salida para MasterCard y un SDT con PK evitaría que tenga que armar una tabla...;tabla que debo cambiar cada vez que master modifica el diseño de sus registros, jeje. En una de esas esta vez tenemos mas suerte!

    ResponderEliminar
  8. Lo de tener SDT con claves, se habia discutido en el foro de la rocha y me habia dicho Gustavo Proto que era una mejora importante y que no iba a estar para la rocha (X).

    Creo que es una funcionalidad muy util y seria bueno seguir investigando su factibilidad.

    El link http://www.gxopen.com/forumsr/servlet/viewthread?ARTECH,22,126696

    ResponderEliminar