Blog Article & Copyright by Lukas Hillesheim
In Teil 1 dieser Serie wird gezeigt, wie Lambda Expressions innerhalb von Power Query Iteratoren funktionieren. Da sehr viele Power Query Funktion Iteratoren sind, werden Lambda Expressions, die innerhalb eines Iterators plaziert werden, wiederholt aufgerufen. Bei jedem Aufruf übergibt der Iterator andere Daten an die Lambda Expression.
Wie SQL, Python, DAX etc. bietet auch Power Query ein Set von Iterator-Funktionen an. Häufig lassen die Funktions-Namen vermuten, dass sich eine Schleife in Ihrem Inneren verbirgt, manchmal jedoch nicht. Und in einigen Fällen ist es sogar schwer, die jeweilige Funktion als Iterator zu verstehen.
1. Iteratoren in Power Query
Funktions-Name mit offensichtlichen Bezug zu einer Schleife. Der Name einer Iterator-Funktion wie z.B. List.Accumulate macht deutlich, dass man der Funktion eine Liste übergibt und die Funktion auf Basis der Liste ein einzelnes Element als Ergebnis ableitet. Es ist offensichtlich, dass zur Herleitung des Ergebnisses eine Schleife ausgeführt wird.
List: { 10, 50, 30 } Result: 90
Die Business Logik innerhalb der Funktion könnte man sich als OOP-Pseudo-Code folgendermassen vorstellen:
Versteckte Iteration Für andere Power Query Funktionen ist es nicht so offensichtlich, dass sie eine Iteration verkapseln. Beispielsweise ist die Power Query Funktion "Table.SplitColumn" in der Lage, aus dem Wert einer einzelnen Spalte mehrere Spalten abzuleiten. Worin würde dabei die Iteration bestehen?
Beispiel: Tabelle, bei der eine Spalte geteilt werden soll:
Die Zeichenfolge "Judy, Munich" in Zeile 1 kann gesplittet werden, wobei ", " als Separator zum Einsatz kommt. Bevor die einzelnen Werte, "Judy" und "Munich", in neuen Spalten plaziert werden können, muss es diese Spalten schon geben. Der erste Schritt vor einer Teilung der Zeichenfolge muss daher die Änderung des Tabellen-Schemas sein, indem zwei neue Spalten hinzugefügt werden. Das letzte Argument der Funktion "Table.SplitColumn" instruiert die Engine, die Spalten "PersonName" und "CityName" zu erstellen:
Schritt 1. Modifizieren des Schemas. Hinzufügen zweier Spalten:
Schritt 2. Füllen der neuen Spalten
Nachdem die beiden Spalten fertig für die Aufnahme neuer Werte sind, kommt die Iteration ins Spiel. Die Business-Logik ausgedrückt in OOP-Pseudo-Code könnte folgendermassen aussehen:
Nachdem die Schleife beendet ist, sind die beiden neuen Spalten gefüllt und die originale Quell-Spalte kann gelöscht werden:
2. Lambda Expressions
Die MSDN Syntax-Hilfe für "Table.SplitColumns":
- Table.SplitColumn(
- table as table,
- sourceColumn as text,
- splitter as function,
- optional columnNamesOrNumber as any,
- optional default as any,
- optional extraColumns as any) as table
Im Power Query Code-Beispiel von oben werden von den 6 möglichen Argumenten nur die ersten 4 verwendet:
- - 'table'-Argument: "customer"
- - 'sourceColumn'-Argument: "Person"
- - 'function'-Argument: Splitter.SplitTextByDelimiter
- - 'columnNamesOrNumber'-Argument: "PersonName", "CityName"
Die Syntax-Hilfe informiert darüber, dass Argument 3 vom Typ "function" ist.
Immer dann, wenn man ein Argument vom Typ "function" sieht, bedeutet dies, dass man an dieser Stelle eine Funktion wie z.B. "Splitter.SplitTextByDelimiter" einsetzen kann. Allerdings ist man nicht nur auf BuiltIn-Funktionen beschränkt, sondern syntaktisch sind hier auch benutzerdefinierte Ausdrücke vom Typ "function" - also Lambda Expressions - erlaubt.
Allgemeine Syntax für Lambda Expressions:
“=>” ist der Lambda-Operator. Zusammen mit den Ausdrücken auf der linken und rechten Seite ergibt sich eine Lambda-Expression, einem wichtigen Konstrukt von Power Query. Mit Lambda-Expressions kann komplexer Code geschrieben werden und es können Fragestellungen gelöst werden, die über die Fähigkeiten von Oberfläche oder BuiltIn-Funktionen hinausgehen. Neben der nativen Lambda-Syntax existieren auch Syntax-Alternativen, die im Allgemeinen häufiger verwendet werden. Sie sind kompakter und scheinen Anfänger besser verständlich zu sein. Sie werden am Schluss des Artikels erklärt.
Eine Lambda Expression besteht aus drei Teilen: auf der linken Seite des Lambda-Operators "=>" befinden sich runde Klammern, die keine, eine oder mehrere Variablen einschliessen. Wie bei jeder Variablen können Sie einen passenden Namen festlegen. Dann folgt der Lambda-Operator "=>". Auf der rechten Seite des Lambda-Operators befindet sich eine Expression, die die Variablen der linken Seite verwenden kann.
Der Code oben kann unter Verwendung der nativen Syntax folgendermassen umgeschrieben werden:
3. Zusammenwirken von Iteratoren und Lambda Expressions
Schritt 1: Der Iterator - im Beispiel "Table.SplitColumn" - ruft in jedem Durchlauf die Lambda-Expression auf. In Durchlauf 1 wird der Wert "Judy, Munich" an die Lambda-Expression übergeben. Die Variable "column" auf der linken Seite der Lambda-Expression ist dafür zuständig, den Wert entgegenzunehmen.
Schritt 2: Der Ausdruck auf der rechten Seite des Lambda-Operators - im Beispiel "Splitter.SplitTextByDelimiter" - verwendet die Lambda-Variable "column" als Input-Argument. Im nächsten Schritt sorgt die Funktion "Splitter.SplitTextByDelimiter" für das Splitting der Zeichenfolge und gibt als Ergebnis ein String-Array zurück - für die erste Zeile im Beispiel { "Judy", "Munich" }. Das Array wird von der äußeren Funktion "Table.SplitColumn" entgegen genommen und weiter verarbeitet, indem die beiden Werte in die entsprechenden neuen Spalten geschrieben werden. Dieser Vorgang wiederholt sich entsprechend für jede Zeile der Tabelle.
Es gibt in Power Query eine grundsätzliche Herausforderung bei der Verwendung von Lambda Expressions. Es ist zwar klar, dass eine äußere Funktion wie "Table.SplitColumn" Werte an die Lambda-Variablen übergeben kann und auch Rückgabe-Werte entgegennimmt. Die Syntax-Hilfe der MSDN gibt aber keine Auskunft über die Details des unsichtbaren Verkehrs:
- - Es ist nicht klar, wie viele Variablen die Lambda Expression zur Verfügung stellen muss (0, 1, n)
- - Es ist nicht klar, welche Daten-Typen an die Lambda Variable übergeben werden
- - Es ist nicht klar, welchen Typ die Lambda Expression zurückgeben muss
Fehler und Missverständnisse. Das nächste Beispiel zeigt, dass der Entwickler einen schlechten Namen für die Variable gewählt hat. Der Code funktioniert, aber der Variablen-Name zeigt eine Art von Missverständniss. Es ist nicht die Zeile, die an die Variable übergeben wird, sondern eine einzelne Spalte.
Es könnte eine gute Vorgehensweis sein, einer Variablen grundsätzlich zunächst einen generischen Namen zu vergeben, wenn man mit dem Coding beginnt. Z.B. könnte man immer den Variablen-Namen "item" auf der linken Seite plazieren. Später, wenn dann klar ist, was die Variable enthält, könnte man der Variablen einen passenderen Namen vergeben wie z.B. "column".
Falsche Anzahl an Variablen:
Der einzige Weg, die versteckte Signatur der Lambda Expression zu verstehen, ist Trial and Error. Zum Glück sind die BuiltIn Funktionen sauber benannt. Es ist daher intuitiv, die Signatur mehr oder weniger zu erraten. Wenn zum Beispiel eine Funktion wie "Table.SplitColumn" aus einer einzelnen Zeichenfolge Werte für mehrere neue Spalten generiert, erwartet "Table.SplitColumn" von der Lambda-Expression offensichtlich als Rückgabe-Wert eine Liste. Und sicherlich muss die Liste für jede Zeile der äußeren Funktion die gleiche Anzahl an Werten zurückgeben. Wenn zwei neue Spalten zu füllen sind, sollte die Lambda Expression wohl eine Liste mit zwei Werten zurückgeben.
4. Alternative Syntax
Each-Syntax. Anwender tendieren dazu, die "Each-Syntax" zu benutzen. Bei der "Each-Syntax" werden Variablen-Deklaration und Lambda-Operator durch den Ausdruck "Each" ersetzt.
each <expression>
Auf der rechten Seite kann die generische Variable "_" verwendet werden. Die Variable "_" wird implizit auf der linken Seite erstellt und durch den Iterator gefüllt. Einigen Anwendern kommt die Variable sicherlich bekannt vor: in PowerShell existiert ein ähnliches Konzept. Das "Each"-Schlüsselwort verdeutlicht, dass die Lambda Expression innerhalb einer Schleife aufgerufen wird.
5. Benutzerdefinierte Funktion als Lambda Expression
Lambda Expressions sind nützlich, wenn es schwer oder unmöglich ist, eine Aufgabe mit Hilfe von BuiltIn-Funktionen zu lösen. Im folgenden Beispiel wird die BuiltIn-Funktion "Splitter.SplitTextByDelimiter" innerhalb einer Lambda Expression benutzt. Der Business Case sieht vor, nicht nur das Splitting durchzuführen, sondern während der Operation auch gleich mögliche NULL-Werte mit zu ersetzen. Einige Personen-Namen enthalten NULL und einige Städte ebenfalls. Während der Split-Operation sollen diese NULL-Werte durch "n/a" ersetzt werden.
Die Lambda-Expression der rechten Seite besteht aus mehreren Anweisungen. Drei Schritte werden im let-Block durchgeführt, um die Rückgabe vorzubereiten. Dabei wird u.a. auch die Splitter-Funktion benutzt und das Auftreten von NULL-Werten abgefangen. Der in-Block gibt am Schluss als Ergebnis das String-Array aus Person und Stadt zurück.