martes, 10 de enero de 2023

Power Query: Each o Function

Este es un ar´ticulo que seguramente debería haber escrito bastante antes, ya que me parece muy importante para trabajar correctamente con muchas de las funciones M existentes...
Hablaré en esta ocasión de la sintaxis Each _ vs su alternativa Function.

En multitud de ejemplos que hayamos podido trabajar, recurrentemente hemos empleado la sintaxis Each _ que según el contexto de uso podría ser:
- un registro (o fila), si trabajamos en el contexto de una tabla
- un elemento si trabajamos sobre una lista
Esto es, básicamente, Tablas y Listas, serían los dos contextos donde el concepto de Each _, entendido como 'cada elemento de una colección', tiene sentido...
Si bien, en ciertos casos, también se podría aplicar al trabajar sobre las partes de un Record.

Teniendo claro esta idea de lo que implica la sintaxis de 'Each _', debemos saber que Power Query lo lee e interpreta como si fuera una función, es decir, evalúa o transforma cada elemento de esa colección.
De hecho, en la declaración de las funciones M, lo normal es que el argumento requerido exija una función!!.
Y este es el punto importante, puesto que es posible (y recomendable en muchas ocasiones) cambiar la forma de expresar ese recorrido por nuestra colección por la sintaxis de las funciones en M, que en su forma más simple responde a la estructura:
( nombre_variable ) => tratamiento_de nombre_variable
i.e., declaramos el nombre de nuestra variable (usaremos uno que sea descriptivo de su uso) entre paréntesis ,seguido de los signos => y a continuación la transformación o cálculo deseado sobre nuestra variable.
Recuerda que esa variable representará una cosa u otra según el contexto!!.

Veamos algunos ejemplos...
Cargaremos los datos de una sencilla tabla 'Tabla1' con cuatro campos: Cliente, Artículo, Unidades y Precio Unitario
Power Query: Each o Function


Veamos nuestro código creado en una Consulta en blanco dentro del editor de Power Query:
let
    Origen = Excel.CurrentWorkbook(){[Name="Tabla1"]}[Content],
    //Uso de la sintaxis Each _
    //donde _ corresponde a cada registro de la tabla 'Origen'
    //y como sobre cualquier Record, podemos llamar a cualquiera de sus campos
    Test1=Table.AddColumn(
                Origen,
                "Prueba1", 
                each _[Unidades] * _ [Precio Unitario]),

    //Uso alternativo de Funciones
    //Declaro una variable, con el nombre 'registro'
    //Y para facilitar la lectura empleo let...in..
    //asignando valor a dos elementos 'uds' y 'pz'
    // a partir de la variable anterior
    // para finalmente devolver el producto de ambos
    Test2=Table.AddColumn(
                Test1,
                "Prueba2", 
                (registro)=> 
                    let 
                        uds=registro[Unidades],
                        pz=registro[Precio Unitario]
                    in 
                        uds * pz )

in
    Test2

Power Query: Each o Function

El resultado, obviamente, es el mismo en los dos casos descritos: Test1 y Test2
Power Query: Each o Function

Ambas alternativas tienen sus pros y sus contras...
Ventaja de Each: fácil de escribir
Inconveniente de Each: en ocasiones difícil de leer o interpretar

Ventaja de usar funciones: facilita la lectura e interpretación, sobre todo en contextos múltiples.
Inconveniente de las funciones: algo más largo de escribir

Quizá aún no estés convencido de las bondades de emplear la sintaxis de funciones en lugar de Each...
Veamos pues un par de ejemplos más.
let
    Origen = Excel.CurrentWorkbook(){[Name="Tabla1"]}[Content],
    //Empleando la sintaxis de funciones
    //obtendremos el producto de cada producto por el precio máximo de cualquier venta de nuestra tabla
    Test3=Table.AddColumn(
                Origen,
                "Prueba3", 
                (registro)=> 
                    let 
                        uds=registro[Unidades],
                        pz=List.Max(Origen[Precio Unitario])
                    in 
                        uds * pz ),

    //DOBLE Each_ DOBLE Función
    //En este caso existe un doble recorrido
    //en su parte más profunda recorremos y seleccionamos aquellos registros que cumplan una condición
    //sobre un elemento del recorrido de los registros del bucle principal
    Test4=Table.AddColumn(
                Test3,
                "Prueba4", 
                (registro)=> 
                    let 
                        uds=registro[Unidades],
                        //art=registro[Artículo],
                        pz=List.Max(Table.SelectRows(
                                        Test3, 
                                        (seleccion)=> seleccion[Artículo]=registro[Artículo])[Precio Unitario])
                                        //(seleccion)=> seleccion[Artículo]=art)[Precio Unitario])
                    in 
                        uds * pz )
in
    Test4

Fíjate en estos ejemplos, donde empleando las funciones y declarando nuestras variables, se clarifica qué estamos haciendo... además, si nos apoyamos en la estructura let..in..., podremos construir situaciones más complejas.

Como ejemplo diferente tenemos el 'Test4', donde recorremos cada fila de nuestra tabla, recuperando el valor de cada 'artículo' y 'número de unidades', y para recuperar el 'precio' optamos por un nuevo proceso de iteración por la tabla para obtener el mayor de los precios solo de aquellos registros coincidentes con el del registro principal actual!!.

En el código descrito he dejado comentado una forma alternativa (más clara a mi forma de ver) que requiere de una línea más de código.

Espero haber aclarado algunas ideas a este respecto y te haya sido de utilidad.

No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.