Funciones de Iteración en Lenguaje M.
En el presente artículo te contaré de restructuración que tendrá nuestra capacitación: Magíster en Lenguaje DAX, hemos tenido la oportunidad de ser formadores en este lenguaje desde el año2014, y en los dos últimos años hemos reorganizado la metodología y realizado diversas pruebas.
— TABLA DE CONTENIDO DE ESTE ARTÍCULO
1. Mecanismo de Iteración
a. Ilustración de Iteraciones
2. Funciones de Iteración en Lenguaje M
a. Table.SelectRows
b. Argumento Función de Table.SelectRows
c. El Desglose del Mini-Reto
d. Ejemplos y Detalles con Table.SelectRows
3. Listado de Funciones de Iteración en Lenguaje M
El mecanismo de iteración no es ajeno a ningún lenguaje bien sea de forma directa o indirecta, llámese lenguaje de programación o lenguaje de funciones.
En el lenguaje DAX, es claro, o por lo menos es mucho más extendido que, existen un conjunto de funciones que iteran.
Si deseas aprender DAX desde cero, te recomiendo nuestra capacitación de 10 horas y 100% gratuita: Lenguaje DAX de Cero a Guerrero, allí conocerás las funciones de iteración y demás.
Pero no nos vayamos por las ramas, porque, en el lenguaje M esta historia es diferente, dado que las funciones de iteración son más escurridizas, y no se habla tan extensamente de ellas como si ocurre en el lenguaje DAX.
Entremos en materia, pero antes:
Advertencia: En el presente artículo asumo que el lector tiene conocimiento previo en el lenguaje M, por lo menos básico, es decir, bloque de trabajo, tipos de valores y funciones.
Mi estimado lector@ si no tiene este conocimiento, le recomendamos que este muy atento a la segunda temporada de la semana de Power Query, ya que allí tendremos un día donde estaremos aprendiendo los fundamentos del lenguaje M.
Mecanismo de Iteración
Hasta el día de hoy Las iteraciones en el lenguaje M consiste en ir haciendo un recorrido en algún tipo de valor, y en cada una de esas paradas (iteraciones) se va ejecutando una tarea específica, que en Power Query cosiste en calcular una expresión del lenguaje.
Se entenderá mejor si lo ilustramos:
Ilustración de Iteraciones
Una función en el lenguaje M puede iterar en una tabla (aunque no exclusivamente) realizando un recorrido fila por fila, así:
Como se puede apreciar, representamos la iteración y la parte de la tabla que involucra la misma con un rectángulo de color café, esto para tener una ayuda visual de lo qué está pasando en tres momentos distintos de la ejecución.
Ahora bien:
No todas las funciones del lenguaje M iteran.
Funciones de Iteración en Lenguaje M
Denominaremos iteradores a un grupo selecto de funciones, y que provienen de las funciones de tabla (Table.X), listas (List.X) y registro (Record.X)
Como preparación previa al escenario, creemos una sencilla tabla de forma manual en el editor avanzado de la mano de la función #table (puedes estudiar esta función aquí):
let
s1 =
#table (
{ «Libro» , «Año», «Precio» } ,
{
{ «Tablas Dinámicas La Quinta Dimensión», 2015 , 40 } ,
{ «El ADN de Power Pivot», 2016 , 60 } ,
{ «El ADN de Power Query», 2019 , 60 }
}
)
in
s1
Table.SelectRows
La función Table.SelectRows aparte de ser una función muy popular, aparece a cada rato en nuestro código M sin que nos debemos cuenta, dado que acciones como utilizar filtros, eliminar filas en blanco, entre otras operaciones derivan en esta función internamente.
Complementariamente: la función Table.SelectRows, es también una función de iteración.
Veamos su sintaxis:
Como se puede apreciar en su sintaxis, la función Table.SelectRows tiene dos argumentos:
.
- Tabla (Como tipo de valor tabla): Es la tabla proporciona donde se va a iterar.
- Condición (Como tipo de valor función): Es la expresión M que se ira ejecutando en cada una de las iteraciones.
.
Es interesante observar que el segundo argumento es un valor de tipo función, y como sabemos una función recibe un o más argumentos de entrada de algunos de los tipos de valores, y arroja como resultado otro tipo de valor.
Hago un paréntesis para comentar los tipos de valores que tenemos en el lenguaje M. Todos ellos los podemos clasificar entres grandes clases.
.
- Valores Primitivos: Numéricos, Fecha, Hora, Fecha y Hora, Fecha/Horza/Zona, Duración, Texto, Nulo y Binarios.
- Valores Estructurados: Listas, Registros y Tabla.
- Valores Abstractos: Funciones y Tipos.
.
El asunto es que, la propia función Table.SelectRows recibe dos argumentos, el primero de tipo tabla, y, el segundo de tipo función, con ello retorna como resultado un valor de tipo tabla con ciertas filas seleccionados
Entrada: Tabla, Función → Salida: Tabla.
El punto es:
La función del segundo argumento de Table.SelectRows:
— ¿Cuáles y de qué tipo de valor recibe, además, que tipo de valor retorna?
Argumento Función de Table.SelectRows
La anterior es una pregunta legítima.
Pregunta que en la referencia de Microsoft no se responde con claridad, por no decir, que es carente de dicha información en su totalidad.
Por ello, en la imagen anterior de la sintaxis de la función Table.SelectRows, vemos una etiqueta en el segundo argumento de color café oscuro que lo aclara, mejor dicho, lo resalto a continuación:
El segundo argumento (condición) de Table.SelectRows que es una función recibe un único argumento que es un valor de tipo registro (record) y arroja como resultado final un valor de tipo lógico (true o false).
Deducimos que lo que alimenta el argumento de la función interna de Table.SelectRows es la iteración actual, y si abogamos a nuestro conocimiento del lenguaje M será claro que le está pasando la fila en la iteración actual como un valor de registro, porque recordemos:
Las filas son valores de tipo registros y las columnas valore de tipo lista.
Pero …
— ¿Qué significa el valor de tipo lógico que devuelve como resultado final?
La respuesta a esta pregunta es fácil de dilucidar, dado que, Table.SelectRows retorna un conjunto de filas
Lo anterior quiere decir que la función Table.SelectRows devuelve en su resultado final aquellas filas que devolvieron true en su evaluación en la iteración, y elimina o deja fuera del campo de visión aquellas filas cuya iteración retornaron false en la evaluación de la función del segundo argumento.
Con lo cual si nos ponemos el siguiente reto:
RETO: Tomar la tabla inicial en el editor avanzado y devolver aquellas filas cuya letra en la cuarta posición de la palabra (contando de izquierda derecha) de la columna Libro sea la letra a, no importa si es mayúscula o mayúscula .
Podemos definir el código M, hasta aquí así:
let
s1 =
#table (
{ «Libro» , «Año», «Precio» } ,
{
{ «Tablas Dinámicas La Quinta Dimensión», 2015 , 40 } ,
{ «El ADN de Power Pivot», 2016 , 60 } ,
{ «El ADN de Power Query», 2019 , 60 }
}
) ,
s2 =
Table.SelectRows (
s1 ,
_____________
)
in
s2
.
Quedando en pendiente la parte: _______ que es el segundo argumento de Table.SelectRows en el segundo paso de transformación.
Para crear dicha función es necesario tener conocimiento de cómo se hace, por lo que si no conoces los fundamentos para la creación de una función personalizada en Power Query, te recomiendo que te leas este artículo y luego regreses al presente manuscrito.
O si cuentas con nuestro libro El ADN de Power Query, dale una leía al capítulo 17.
La solución:
let
s1 =
#table (
{ «Libro» , «Año», «Precio» } ,
{
{ «Tablas Dinámicas La Quinta Dimensión», 2015 , 40 } ,
{ «El ADN de Power Pivot», 2016 , 60 } ,
{ «El ADN de Power Query», 2019 , 60 }
}
) ,
s2 =
Table.SelectRows (
s1 ,
( FilaActual as record ) as logical =>
Text.Middle ( FilaActual[Libro] , 3 , 1) = «a» or
Text.Middle ( FilaActual[Libro] , 3 , 1) = «A»
)
in
s2
El Desglose del Mini-Reto
Lo vital es que la función devuelva true o false.
La función Text.Middle es como la función EXTRAE en Excel, es decir, le damos un texto en el primer argumento, en el segundo argumento le indicamos desde cuál posición queremos retornar para en el último argumento señalar cuántas posiciones retornar a partir de allí
La diferencia con EXTRAE es que Text.Middle cuanto a partir del cero.
Además, dado que tenemos que se está guardando la fila en la iteración actual en el argumento FilaActual, podemos llamar a ese texto indicando en el operador de búsqueda de registro (paréntesis cuadrados) la columna Libro.
Como queremos que sea mayúscula o minúscula podemos indicamos las dos condiciones con el operador lógico or.
La función puede tener más de un paso y puede ser tan compleja como se necesite, además, podemos utilizar el syntax sugar each para simplificar, así:
let
s1 =
#table (
{ «Libro» , «Año», «Precio» } ,
{
{ «Tablas Dinámicas La Quinta Dimensión», 2015 , 40 } ,
{ «El ADN de Power Pivot», 2016 , 60 } ,
{ «El ADN de Power Query», 2019 , 60 }
}
) ,
s2 =
Table.SelectRows (
s1 ,
each
Text.Middle ( [Libro] , 3 , 1) = «a» or
Text.Middle ( [Libro] , 3 , 1) = «A»
)
in
s2
.
Sin embargo, iré desglosando cada aspecto con total detalle en próximos artículos y seminarios web.
Ejemplos y Detalles con Table.SelectRows
Abordemos un par de ejemplos adicionales
Ejemplo 2: Dada una tabla almacenada en el identificador s1 donde se encuentra un columna con nombre utilidad, devolver dicha tabla sólo con aquellas cuyo valor de utilidad estrictamente mayor al promedio de la columna utilidad.
Solución:
let
s1 =
Table.FromRecords (
{
[ Costo = 2 , Utilidad = 4 ] ,
[ Costo = 1 , Utilidad = 4 ] ,
[ Costo = 2 , Utilidad = 5 ] ,
[ Costo = 3 , Utilidad = 6 ] ,
[ Costo = 1 , Utilidad = 4 ] ,
[ Costo = 2 , Utilidad = 7 ] ,
[ Costo = 4 , Utilidad = 8 ]
}
) ,
s2 =
Table.SelectRows (
s1 ,
( FilaActual as record ) as logical =>
FilaActual[Utilidad] > List.Average ( s1[Utilidad] )
)
in
s2
.
«Declarar» el tipo de valor que admite el argumento (record) y el valor de salida (logical) no es obligatorio, es decir, la expresión anterior se puede escribir así:
let
s1 =
Table.FromRecords (
{
[ Costo = 2 , Utilidad = 4 ] ,
[ Costo = 1 , Utilidad = 4 ] ,
[ Costo = 2 , Utilidad = 5 ] ,
[ Costo = 3 , Utilidad = 6 ] ,
[ Costo = 1 , Utilidad = 4 ] ,
[ Costo = 2 , Utilidad = 7 ] ,
[ Costo = 4 , Utilidad = 8 ]
}
) ,
s2 =
Table.SelectRows (
s1 ,
( FilaActual ) =>
FilaActual[Utilidad] > List.Average ( s1[Utilidad] )
)
in
s2
.
Hacerlo tienes sus ventajas en ciertos casos de creación de funciones personalizadas que dan cara al usuario.
Por otra parte, nótese que el valor de función en el segundo argumento no tiene un identificador, esto es gracias al ambiente, y por ello esta función es anónima, porque no tiene ningún identificador asociado. Más sobre ambiente en el capítulo 14 del libro El ADN de Power Query.
Este artículo es tan sólo la punta del iceberg.
Por lo pronto te dejo el:
Lista de Funciones de Iteración (De Tabla) en Lenguaje M
La lista siguiente no incluye todas las funciones de iteración del lenguaje M, pero si la lista completa de funciones de iteración de tabla.
Table.AddColumns, Table.AddJoinColumn, Table.CombineColumns, Table.FirstN, Table.Group, Table.LastN, Table.MatchesAllRows, Table.MatchesAnyRows, Table.MaxN, Table.MinN, Table.Partition, Table.PartitionValues, Table.Pivot, Table.RemoveFirstN, Table.RemoveLastN, Table.ReplaceValue, Table.SelectRows, Table.Skip, Table.SplitColumn y Table.TransformRows.
Como las demás funciones en las otras categorías de tipo de valores requieren su estudio particular en iteración, iremos ampliando la lista en futuros artículos donde lo estudiemos.
Eso es todo por ahora con el artículo.
Por favor, comenta y regálame tu feedback, sugerencias, correcciones, etc. Qué cosas o elementos se me escapan y cuáles datos adicionales pueden complementar esta guía, cuáles son tus comentarios, experiencias, etc.
Estaré leyendo y respondiendo todos los comentarios activamente.
— Miguel Caballero