<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>article Lambda functions: Knights of the higher-order in Technical Blog</title>
    <link>https://community.databricks.com/t5/technical-blog/lambda-functions-knights-of-the-higher-order/ba-p/61264</link>
    <description>&lt;P&gt;&lt;SPAN&gt;It’s not often that a DBMS surprises me when it comes to SQL; I kind of think I have seen it all. However&lt;/SPAN&gt;&lt;SPAN&gt;&amp;nbsp;there is this one feature in Spark SQL that made me go: &lt;FONT face="comic sans ms,sans-serif"&gt;“Huh! Now that’s cool!”&lt;/FONT&gt; when I first encountered it.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;In fact, I am baffled that, as far as I know, Spark SQL is the only SQL dialect that has this capability:&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;SPAN&gt;&lt;EM&gt;Higher-order functions&lt;/EM&gt; and &lt;EM&gt;Lambda functions&lt;/EM&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Higher-order functions - which take functions as arguments - are not new outside of SQL. A&lt;/SPAN&gt;&lt;SPAN&gt;mazing things can be done in C by passing functions, and&lt;/SPAN&gt;&lt;SPAN&gt;&amp;nbsp;I knew lambda functions - which are functions without a name - since my &lt;EM&gt;LISP&lt;/EM&gt; days at University in the early 90’s.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Let’s take a look into this unsung corner of SQL and what it can do.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt; You can find a notebook with all the SQL &lt;A href="https://github.com/srielau/notebooks/blob/main/LAMBDA%20FUNCTION%20BLOG.sql" target="_self"&gt;here&lt;/A&gt;.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;Example: quicksort&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;The perhaps most famous example of a higher order function is &lt;STRONG&gt;qsort()&lt;/STRONG&gt; residing in C's &lt;STRONG&gt;stdlib&lt;/STRONG&gt;:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;void qsort(void *, size_t, size_t,&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int (*)(const void *, const void *));&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;Chances are you used it by choice, in an interview, or as classwork. As you see or remember: &lt;STRONG&gt;qsort()&lt;/STRONG&gt; takes a comparator function as an argument.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;The function accepts pointers to two values,&amp;nbsp;&lt;STRONG&gt;a&lt;/STRONG&gt; and &lt;STRONG&gt;b,&lt;/STRONG&gt;&amp;nbsp;to compare and it returns an integer.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;The comparator returns a number whose sign indicates the outcome.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Positive means&amp;nbsp; "&lt;STRONG&gt;a &amp;gt; b&lt;/STRONG&gt;", 0 means "&lt;STRONG&gt;a == b&lt;/STRONG&gt;" and negative means "&lt;STRONG&gt;a &amp;lt; b&lt;/STRONG&gt;".&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;The definition of the actual comparison is left entirely up to the function. &lt;STRONG&gt;qsort()&lt;/STRONG&gt; does not prescribe the meaning.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Here is how it’s used (courtesy of chatGPT):&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;#include &amp;lt;stdio.h&amp;gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;int compareIntegers(const void *a, const void *b) {&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return (*(int *)a - *(int *)b);&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;}&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;int main() {&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int array[] = {5, 2, 8, 1, 3};&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;size_t arraySize = sizeof(array) / sizeof(array[0]);&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;qsort(array, arraySize, sizeof(array[0]), compareIntegers);&lt;BR /&gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (size_t i = 0; i &amp;lt; arraySize; ++i) {&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("%d ", array[i]);&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;BR /&gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;}&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;So how do we do this in SQL? The equivalent of &lt;STRONG&gt;qsort()&lt;/STRONG&gt; in SQL is &lt;STRONG&gt;array_sort(array, func)&lt;/STRONG&gt;.&lt;BR /&gt;&lt;BR /&gt;Unlike &lt;STRONG&gt;qsort()&lt;/STRONG&gt; which takes a &lt;EM&gt;named function&lt;/EM&gt; as argument, &lt;STRONG&gt;array_sort()&lt;/STRONG&gt; takes a &lt;EM&gt;lambda function&lt;/EM&gt;. Here is the SQL implementation of the example above:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; array_sort(array(5, 2, 8, 1, 3),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) -&amp;gt; &lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt; - &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;);&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;FONT color="#993366"&gt;&lt;SPAN&gt;[1, 2, 3, 5, 8]&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;This is very dense! (&lt;span class="lia-unicode-emoji" title=":grinning_face_with_sweat:"&gt;😅&lt;/span&gt; We omitted NULL handling, but so did chatGPT...&amp;nbsp;&lt;span class="lia-unicode-emoji" title=":thinking_face:"&gt;🤔&lt;/span&gt;)&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;Lambda Functions&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;A lambda function is a special kind of expression and uses the &lt;STRONG&gt;-&amp;gt;&lt;/STRONG&gt;&amp;nbsp;infix operator.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;Syntax&lt;/SPAN&gt;&lt;/H2&gt;
&lt;PRE&gt;&lt;SPAN&gt;{ param -&amp;gt; expr |&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;(param1 [, ...] ) -&amp;gt; expr }&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;On the left the operator expects an identifier or a tuple of identifiers naming the parameters, e&lt;/SPAN&gt;&lt;SPAN&gt;.g.,&amp;nbsp;&lt;STRONG&gt;a&lt;/STRONG&gt; or &lt;STRONG&gt;(a, b)&lt;/STRONG&gt;.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Note that, unlike in a SQL UDF definition, this signature of the lambda function does not include any types!&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;The lambda function expects that the higher-order function will tell it what these types must be.&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;In the &lt;STRONG&gt;array_sort()&lt;/STRONG&gt; example above, the type is &lt;FONT face="andale mono,times"&gt;INTEGER&lt;/FONT&gt; because the function was passed an &lt;FONT face="andale mono,times"&gt;ARRAY&amp;lt;INTEGER&amp;gt;&lt;/FONT&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;On the right side of &lt;STRONG&gt;-&amp;gt;&lt;/STRONG&gt; is the implementation of the function. It is an expression that uses the parameters of the lambda function to compute a result.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;In some cases the type of the result will determine the result type of the higher-order function.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Other times, like in the case of &lt;STRONG&gt;array_sort()&lt;/STRONG&gt;, the higher order function expects a certain type and specific result values.&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;In most cases lambda functions tend to be self-contained. That is, the expression is solely based on the parameters. But that is not actually required.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;For example we can influence the sort order using a temporary variable:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;DECLARE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;sortorder&lt;/FONT&gt; = -1;&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; array_sort(array(5, 2, 8, 1, 3),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) -&amp;gt; (&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt; - &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) * &lt;FONT color="#FF9900"&gt;sortorder&lt;/FONT&gt;);&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;FONT color="#993366"&gt;&lt;SPAN&gt;[8, 5, 3, 2, 1]&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;Or we can use a column reference, even a lateral correlation:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; *&amp;nbsp;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#008000"&gt;VALUES&lt;/FONT&gt;(-1), (1) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;t&lt;/FONT&gt;(&lt;FONT color="#FF9900"&gt;sortorder&lt;/FONT&gt;),&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;FONT color="#008000"&gt;LATERAL&lt;/FONT&gt; (&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; array_sort(array(5, 2, 8, 1, 3),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) -&amp;gt; (&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt; - &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) * &lt;FONT color="#FF9900"&gt;sortorder&lt;/FONT&gt;));&lt;/SPAN&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;SPAN&gt;&amp;nbsp;1 [1, 2, 3, 5, 8]&lt;/SPAN&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;SPAN&gt;&lt;FONT color="#993366"&gt;-1 [8, 5, 3, 2, 1]&lt;/FONT&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;Yo can even use subqueries in the lambda function expression.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;User defined aggregates: Maximum distance on a plane.&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;While you can define user-defined aggregates using Scala UDF, there is no API to accomplish this using SQL... o&lt;/SPAN&gt;&lt;SPAN&gt;r is there?&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Databricks provides a &lt;STRONG&gt;reduce()&lt;/STRONG&gt; (or &lt;STRONG&gt;aggregate()&lt;/STRONG&gt;) function that takes an array and allows us to pass an&lt;EM&gt; initial value&lt;/EM&gt;, an &lt;EM&gt;aggregation&lt;/EM&gt; lambda function, and a&lt;EM&gt; finalization&lt;/EM&gt;&amp;nbsp;lambda function.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;This is pretty much what user-defined aggregation functions do!&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;All we need to do is first aggregate a group into an array using&lt;STRONG&gt; array_agg()&lt;/STRONG&gt;, and then use &lt;STRONG&gt;reduce()&lt;/STRONG&gt; to collapse the array into the desired scalar result:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; reduce(array_agg(struct(&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt;)),&amp;nbsp;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;named_struct('x', null::integer, 'y', null::integer, 'len', null::integer),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;point&lt;/FONT&gt;) -&amp;gt; &lt;FONT color="#008000"&gt;CASE WHEN&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc.len&lt;/FONT&gt; &lt;FONT color="#008000"&gt;IS NULL&lt;BR /&gt;                                     OR&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc.len&lt;/FONT&gt; &amp;lt; &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; + &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.y&lt;BR /&gt;&lt;/FONT&gt;&lt;FONT color="#008000"&gt;                                   THEN&lt;/FONT&gt; named_struct('x', &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt;, 'y', &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt;,&lt;BR /&gt;                                                     'len', &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt;&amp;nbsp;+ &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt;)&lt;BR /&gt;                                   &lt;FONT color="#008000"&gt;ELSE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt; &lt;FONT color="#008000"&gt;END&lt;/FONT&gt;,&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt; -&amp;gt; struct(&lt;FONT color="#FF9900"&gt;acc.x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;acc.y&lt;/FONT&gt;))&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#008000"&gt;VALUES&lt;/FONT&gt;(1, 10), (2, -10), (-10, 3) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;points&lt;/FONT&gt;(&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt;);&lt;BR /&gt;&lt;FONT color="#993366"&gt;{ x: -10, y: 3 }&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;What’s going on here exactly?&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;We define the shape and initial state of the accumulator which is also our scratchpad.&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;In this case, we decide to not only collect the interim max but also the computed length of the vector (or rather its square since we don’t need the actual length).&amp;nbsp;&amp;nbsp;&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;The accumulator lambda function needs to parameters:&lt;/SPAN&gt;&lt;/LI&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="2"&gt;&lt;SPAN&gt;&lt;STRONG&gt;acc&lt;/STRONG&gt;: is the type of the struct of the initial state.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="2"&gt;&lt;SPAN&gt;&lt;STRONG&gt;poin&lt;/STRONG&gt;t: is typed to the element of the array we reduce.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;/UL&gt;
&lt;P class="lia-indent-padding-left-60px"&gt;The implementation of the lambda function is to compare the previous maximum to the point and update it to the point if necessary. It returns the updated &lt;STRONG&gt;acc&lt;/STRONG&gt; value.&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Since we added a &lt;EM&gt;scratchpad&lt;/EM&gt; in the form of the &lt;STRONG&gt;len&lt;/STRONG&gt; field, we use a &lt;EM&gt;final&lt;/EM&gt; lambda function which accepts &lt;STRONG&gt;acc&lt;/STRONG&gt; again and returns whatever we want the &lt;STRONG&gt;reduce()&lt;/STRONG&gt; function to return. In this case, a &lt;FONT face="andale mono,times"&gt;struct&amp;lt;x int, y int&amp;gt;&lt;/FONT&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN&gt;So this works, but it is not pretty. Can we persist the logic in a UDF?&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;CREATE FUNCTION&lt;/FONT&gt; max_distance(&lt;FONT color="#008000"&gt;a&lt;/FONT&gt; array&amp;lt;struct&amp;lt;&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt; int, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt; int&amp;gt;&amp;gt;)&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&lt;FONT color="#008000"&gt;RETURN&lt;/FONT&gt; reduce(&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt;,&amp;nbsp;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; named_struct('x', null::integer, 'y', null::integer, 'len', null::integer),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (&lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;point&lt;/FONT&gt;) -&amp;gt; &lt;FONT color="#008000"&gt;CASE WHEN&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc.len&lt;/FONT&gt; &lt;FONT color="#008000"&gt;IS NULL&lt;BR /&gt;                                      OR&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc.len&lt;/FONT&gt; &amp;lt; &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; + &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.y&lt;BR /&gt;&lt;/FONT&gt;&lt;FONT color="#008000"&gt;                                    THEN&lt;/FONT&gt; named_struct('x', &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt;, 'y', &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt;,&lt;BR /&gt;                                                      'len', &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.x &lt;/FONT&gt;+ &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt;)&lt;BR /&gt;&lt;FONT color="#008000"&gt;                                    ELSE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt; &lt;FONT color="#008000"&gt;END&lt;/FONT&gt;,&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt; -&amp;gt; struct(&lt;FONT color="#FF9900"&gt;acc.x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;acc.y&lt;/FONT&gt;));&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; max_distance(array_agg(struct(&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt;)))&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#008000"&gt;VALUES &lt;/FONT&gt;(1, 10), (2, -10), (-10, 3) AS &lt;FONT color="#FF9900"&gt;points&lt;/FONT&gt;(&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt;);&lt;BR /&gt;&lt;FONT color="#993366"&gt;{ x: -10, y: 3 }&lt;/FONT&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;FONT color="#000000"&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt; This isn't the fastest way to perform this particular aggregation, but it serves to illustrate the purpose.&lt;BR /&gt;&lt;BR /&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;What else is there?&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;There are a decent number of higher-order functions that you can use.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Here is a list as of the writing of the blog:&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/aggregate.html" target="_self"&gt;&lt;SPAN&gt;aggregate(expr,start,merge[,finish])&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Aggregates elements in an array using a custom aggregator.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/array_sort.html" target="_self"&gt;&lt;SPAN&gt;array_sort(array,func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Returns the input array sorted according to provided lambda.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/exists.html" target="_self"&gt;&lt;SPAN&gt;exists(expr, pred)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Returns true if &lt;STRONG&gt;pred&lt;/STRONG&gt; is true for any element in &lt;STRONG&gt;expr&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/filter.html" target="_self"&gt;&lt;SPAN&gt;filter(expr,func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Filters the array in &lt;STRONG&gt;exp&lt;/STRONG&gt;r using the provide lambda.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/forall.html" target="_self"&gt;&lt;SPAN&gt;forall(expr, predFunc)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Tests whether &lt;STRONG&gt;predFunc&lt;/STRONG&gt; holds for all elements in the array.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/reduce.html" target="_self"&gt;&lt;SPAN&gt;reduce(expr,start,merge[,finish])&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Aggregates elements in an array using a custom aggregator.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/transform.html" target="_self"&gt;&lt;SPAN&gt;transform(expr, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Transforms elements in an array in &lt;STRONG&gt;expr&lt;/STRONG&gt; using the function &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/zip_with.html" target="_self"&gt;&lt;SPAN&gt;zip_with(expr1, expr2, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Merges the arrays in &lt;STRONG&gt;expr1&lt;/STRONG&gt; and &lt;STRONG&gt;expr2&lt;/STRONG&gt;, element-wise, into a single array using &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/map_filter.html" target="_self"&gt;&lt;SPAN&gt;map_filter(expr, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Filters entries in the map in &lt;STRONG&gt;expr&lt;/STRONG&gt; using the function &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/map_zip_with.html" target="_self"&gt;&lt;SPAN&gt;map_zip_with(map1, map2, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Merges &lt;STRONG&gt;map1&lt;/STRONG&gt; and &lt;STRONG&gt;map2&lt;/STRONG&gt; into a single map.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/transform_keys.html" target="_self"&gt;&lt;SPAN&gt;transform_keys(expr, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Transforms keys in a map in &lt;STRONG&gt;expr&lt;/STRONG&gt; using the function &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/transform_values.html" target="_self"&gt;&lt;SPAN&gt;transform_values(expr, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Transforms values in a map in &lt;STRONG&gt;expr&lt;/STRONG&gt; using the function &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;SPAN&gt;Conclusion&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;Higher-order functions using lambda functions are a very powerful, yet under appreciated feature of Spark SQL.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;They can be used to transform arrays and maps, and perform user-defined aggregations.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;References:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI data-stringify-indent="0" data-stringify-border="0"&gt;&lt;A class="c-link" href="https://www.databricks.com/blog/parameterized-queries-pyspark" target="_blank" rel="noopener noreferrer" data-stringify-link="https://www.databricks.com/blog/parameterized-queries-pyspark" data-sk="tooltip_parent"&gt;Blog: Parameterized queries with PySpark&lt;/A&gt;&lt;/LI&gt;
&lt;LI data-stringify-indent="0" data-stringify-border="0"&gt;&lt;A class="c-link" href="https://www.databricks.com/blog/2018/11/16/introducing-new-built-in-functions-and-higher-order-functions-for-complex-data-types-in-apache-spark.html" target="_blank" rel="noopener noreferrer" data-stringify-link="https://www.databricks.com/blog/2018/11/16/introducing-new-built-in-functions-and-higher-order-functions-for-complex-data-types-in-apache-spark.html" data-sk="tooltip_parent"&gt;Blog: New built in functions and higher order functions in Apache Spark&lt;/A&gt;&lt;/LI&gt;
&lt;LI data-stringify-indent="0" data-stringify-border="0"&gt;&lt;A class="c-link" href="https://www.databricks.com/blog/2017/05/24/working-with-nested-data-using-higher-order-functions-in-sql-on-databricks.html" target="_blank" rel="noopener noreferrer" data-stringify-link="https://www.databricks.com/blog/2017/05/24/working-with-nested-data-using-higher-order-functions-in-sql-on-databricks.html" data-sk="tooltip_parent"&gt;Blog: Working with nested data using higher order functions in Databricks with SQL&lt;/A&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;SQL Ref: Lambda Functions&lt;/SPAN&gt;&lt;/A&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/array_sort.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;SQL Ref: array_sort&lt;/SPAN&gt;&lt;/A&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/reduce.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;SQL Ref: Reduce&lt;/SPAN&gt;&lt;/A&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://github.com/srielau/notebooks/blob/main/LAMBDA%20FUNCTION%20BLOG.sql" target="_self"&gt;Notebook with SQL in this post&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;</description>
    <pubDate>Tue, 27 Feb 2024 21:12:04 GMT</pubDate>
    <dc:creator>SergeRielau</dc:creator>
    <dc:date>2024-02-27T21:12:04Z</dc:date>
    <item>
      <title>Lambda functions: Knights of the higher-order</title>
      <link>https://community.databricks.com/t5/technical-blog/lambda-functions-knights-of-the-higher-order/ba-p/61264</link>
      <description>&lt;P&gt;&lt;SPAN&gt;It’s not often that a DBMS surprises me when it comes to SQL; I kind of think I have seen it all. However&lt;/SPAN&gt;&lt;SPAN&gt;&amp;nbsp;there is this one feature in Spark SQL that made me go: &lt;FONT face="comic sans ms,sans-serif"&gt;“Huh! Now that’s cool!”&lt;/FONT&gt; when I first encountered it.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;In fact, I am baffled that, as far as I know, Spark SQL is the only SQL dialect that has this capability:&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;SPAN&gt;&lt;EM&gt;Higher-order functions&lt;/EM&gt; and &lt;EM&gt;Lambda functions&lt;/EM&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Higher-order functions - which take functions as arguments - are not new outside of SQL. A&lt;/SPAN&gt;&lt;SPAN&gt;mazing things can be done in C by passing functions, and&lt;/SPAN&gt;&lt;SPAN&gt;&amp;nbsp;I knew lambda functions - which are functions without a name - since my &lt;EM&gt;LISP&lt;/EM&gt; days at University in the early 90’s.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Let’s take a look into this unsung corner of SQL and what it can do.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt; You can find a notebook with all the SQL &lt;A href="https://github.com/srielau/notebooks/blob/main/LAMBDA%20FUNCTION%20BLOG.sql" target="_self"&gt;here&lt;/A&gt;.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;Example: quicksort&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;The perhaps most famous example of a higher order function is &lt;STRONG&gt;qsort()&lt;/STRONG&gt; residing in C's &lt;STRONG&gt;stdlib&lt;/STRONG&gt;:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;void qsort(void *, size_t, size_t,&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int (*)(const void *, const void *));&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;Chances are you used it by choice, in an interview, or as classwork. As you see or remember: &lt;STRONG&gt;qsort()&lt;/STRONG&gt; takes a comparator function as an argument.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;The function accepts pointers to two values,&amp;nbsp;&lt;STRONG&gt;a&lt;/STRONG&gt; and &lt;STRONG&gt;b,&lt;/STRONG&gt;&amp;nbsp;to compare and it returns an integer.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;The comparator returns a number whose sign indicates the outcome.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Positive means&amp;nbsp; "&lt;STRONG&gt;a &amp;gt; b&lt;/STRONG&gt;", 0 means "&lt;STRONG&gt;a == b&lt;/STRONG&gt;" and negative means "&lt;STRONG&gt;a &amp;lt; b&lt;/STRONG&gt;".&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;The definition of the actual comparison is left entirely up to the function. &lt;STRONG&gt;qsort()&lt;/STRONG&gt; does not prescribe the meaning.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Here is how it’s used (courtesy of chatGPT):&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;#include &amp;lt;stdio.h&amp;gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;int compareIntegers(const void *a, const void *b) {&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return (*(int *)a - *(int *)b);&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;}&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;int main() {&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int array[] = {5, 2, 8, 1, 3};&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;size_t arraySize = sizeof(array) / sizeof(array[0]);&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;qsort(array, arraySize, sizeof(array[0]), compareIntegers);&lt;BR /&gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (size_t i = 0; i &amp;lt; arraySize; ++i) {&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("%d ", array[i]);&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;BR /&gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;}&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;So how do we do this in SQL? The equivalent of &lt;STRONG&gt;qsort()&lt;/STRONG&gt; in SQL is &lt;STRONG&gt;array_sort(array, func)&lt;/STRONG&gt;.&lt;BR /&gt;&lt;BR /&gt;Unlike &lt;STRONG&gt;qsort()&lt;/STRONG&gt; which takes a &lt;EM&gt;named function&lt;/EM&gt; as argument, &lt;STRONG&gt;array_sort()&lt;/STRONG&gt; takes a &lt;EM&gt;lambda function&lt;/EM&gt;. Here is the SQL implementation of the example above:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; array_sort(array(5, 2, 8, 1, 3),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) -&amp;gt; &lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt; - &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;);&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;FONT color="#993366"&gt;&lt;SPAN&gt;[1, 2, 3, 5, 8]&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;This is very dense! (&lt;span class="lia-unicode-emoji" title=":grinning_face_with_sweat:"&gt;😅&lt;/span&gt; We omitted NULL handling, but so did chatGPT...&amp;nbsp;&lt;span class="lia-unicode-emoji" title=":thinking_face:"&gt;🤔&lt;/span&gt;)&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;Lambda Functions&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;A lambda function is a special kind of expression and uses the &lt;STRONG&gt;-&amp;gt;&lt;/STRONG&gt;&amp;nbsp;infix operator.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;Syntax&lt;/SPAN&gt;&lt;/H2&gt;
&lt;PRE&gt;&lt;SPAN&gt;{ param -&amp;gt; expr |&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;(param1 [, ...] ) -&amp;gt; expr }&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;On the left the operator expects an identifier or a tuple of identifiers naming the parameters, e&lt;/SPAN&gt;&lt;SPAN&gt;.g.,&amp;nbsp;&lt;STRONG&gt;a&lt;/STRONG&gt; or &lt;STRONG&gt;(a, b)&lt;/STRONG&gt;.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Note that, unlike in a SQL UDF definition, this signature of the lambda function does not include any types!&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;The lambda function expects that the higher-order function will tell it what these types must be.&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;In the &lt;STRONG&gt;array_sort()&lt;/STRONG&gt; example above, the type is &lt;FONT face="andale mono,times"&gt;INTEGER&lt;/FONT&gt; because the function was passed an &lt;FONT face="andale mono,times"&gt;ARRAY&amp;lt;INTEGER&amp;gt;&lt;/FONT&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;On the right side of &lt;STRONG&gt;-&amp;gt;&lt;/STRONG&gt; is the implementation of the function. It is an expression that uses the parameters of the lambda function to compute a result.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;In some cases the type of the result will determine the result type of the higher-order function.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Other times, like in the case of &lt;STRONG&gt;array_sort()&lt;/STRONG&gt;, the higher order function expects a certain type and specific result values.&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;In most cases lambda functions tend to be self-contained. That is, the expression is solely based on the parameters. But that is not actually required.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;For example we can influence the sort order using a temporary variable:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;DECLARE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;sortorder&lt;/FONT&gt; = -1;&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; array_sort(array(5, 2, 8, 1, 3),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) -&amp;gt; (&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt; - &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) * &lt;FONT color="#FF9900"&gt;sortorder&lt;/FONT&gt;);&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;FONT color="#993366"&gt;&lt;SPAN&gt;[8, 5, 3, 2, 1]&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;Or we can use a column reference, even a lateral correlation:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; *&amp;nbsp;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#008000"&gt;VALUES&lt;/FONT&gt;(-1), (1) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;t&lt;/FONT&gt;(&lt;FONT color="#FF9900"&gt;sortorder&lt;/FONT&gt;),&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;FONT color="#008000"&gt;LATERAL&lt;/FONT&gt; (&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; array_sort(array(5, 2, 8, 1, 3),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) -&amp;gt; (&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt; - &lt;FONT color="#FF9900"&gt;b&lt;/FONT&gt;) * &lt;FONT color="#FF9900"&gt;sortorder&lt;/FONT&gt;));&lt;/SPAN&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;SPAN&gt;&amp;nbsp;1 [1, 2, 3, 5, 8]&lt;/SPAN&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;SPAN&gt;&lt;FONT color="#993366"&gt;-1 [8, 5, 3, 2, 1]&lt;/FONT&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;Yo can even use subqueries in the lambda function expression.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;User defined aggregates: Maximum distance on a plane.&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;While you can define user-defined aggregates using Scala UDF, there is no API to accomplish this using SQL... o&lt;/SPAN&gt;&lt;SPAN&gt;r is there?&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Databricks provides a &lt;STRONG&gt;reduce()&lt;/STRONG&gt; (or &lt;STRONG&gt;aggregate()&lt;/STRONG&gt;) function that takes an array and allows us to pass an&lt;EM&gt; initial value&lt;/EM&gt;, an &lt;EM&gt;aggregation&lt;/EM&gt; lambda function, and a&lt;EM&gt; finalization&lt;/EM&gt;&amp;nbsp;lambda function.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;This is pretty much what user-defined aggregation functions do!&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;All we need to do is first aggregate a group into an array using&lt;STRONG&gt; array_agg()&lt;/STRONG&gt;, and then use &lt;STRONG&gt;reduce()&lt;/STRONG&gt; to collapse the array into the desired scalar result:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; reduce(array_agg(struct(&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt;)),&amp;nbsp;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;named_struct('x', null::integer, 'y', null::integer, 'len', null::integer),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;point&lt;/FONT&gt;) -&amp;gt; &lt;FONT color="#008000"&gt;CASE WHEN&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc.len&lt;/FONT&gt; &lt;FONT color="#008000"&gt;IS NULL&lt;BR /&gt;                                     OR&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc.len&lt;/FONT&gt; &amp;lt; &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; + &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.y&lt;BR /&gt;&lt;/FONT&gt;&lt;FONT color="#008000"&gt;                                   THEN&lt;/FONT&gt; named_struct('x', &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt;, 'y', &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt;,&lt;BR /&gt;                                                     'len', &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt;&amp;nbsp;+ &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt;)&lt;BR /&gt;                                   &lt;FONT color="#008000"&gt;ELSE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt; &lt;FONT color="#008000"&gt;END&lt;/FONT&gt;,&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt; -&amp;gt; struct(&lt;FONT color="#FF9900"&gt;acc.x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;acc.y&lt;/FONT&gt;))&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#008000"&gt;VALUES&lt;/FONT&gt;(1, 10), (2, -10), (-10, 3) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;points&lt;/FONT&gt;(&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt;);&lt;BR /&gt;&lt;FONT color="#993366"&gt;{ x: -10, y: 3 }&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;What’s going on here exactly?&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;We define the shape and initial state of the accumulator which is also our scratchpad.&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;In this case, we decide to not only collect the interim max but also the computed length of the vector (or rather its square since we don’t need the actual length).&amp;nbsp;&amp;nbsp;&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;The accumulator lambda function needs to parameters:&lt;/SPAN&gt;&lt;/LI&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="2"&gt;&lt;SPAN&gt;&lt;STRONG&gt;acc&lt;/STRONG&gt;: is the type of the struct of the initial state.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="2"&gt;&lt;SPAN&gt;&lt;STRONG&gt;poin&lt;/STRONG&gt;t: is typed to the element of the array we reduce.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;/UL&gt;
&lt;P class="lia-indent-padding-left-60px"&gt;The implementation of the lambda function is to compare the previous maximum to the point and update it to the point if necessary. It returns the updated &lt;STRONG&gt;acc&lt;/STRONG&gt; value.&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Since we added a &lt;EM&gt;scratchpad&lt;/EM&gt; in the form of the &lt;STRONG&gt;len&lt;/STRONG&gt; field, we use a &lt;EM&gt;final&lt;/EM&gt; lambda function which accepts &lt;STRONG&gt;acc&lt;/STRONG&gt; again and returns whatever we want the &lt;STRONG&gt;reduce()&lt;/STRONG&gt; function to return. In this case, a &lt;FONT face="andale mono,times"&gt;struct&amp;lt;x int, y int&amp;gt;&lt;/FONT&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN&gt;So this works, but it is not pretty. Can we persist the logic in a UDF?&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;CREATE FUNCTION&lt;/FONT&gt; max_distance(&lt;FONT color="#008000"&gt;a&lt;/FONT&gt; array&amp;lt;struct&amp;lt;&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt; int, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt; int&amp;gt;&amp;gt;)&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&lt;FONT color="#008000"&gt;RETURN&lt;/FONT&gt; reduce(&lt;FONT color="#FF9900"&gt;a&lt;/FONT&gt;,&amp;nbsp;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; named_struct('x', null::integer, 'y', null::integer, 'len', null::integer),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (&lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;point&lt;/FONT&gt;) -&amp;gt; &lt;FONT color="#008000"&gt;CASE WHEN&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc.len&lt;/FONT&gt; &lt;FONT color="#008000"&gt;IS NULL&lt;BR /&gt;                                      OR&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc.len&lt;/FONT&gt; &amp;lt; &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; + &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.y&lt;BR /&gt;&lt;/FONT&gt;&lt;FONT color="#008000"&gt;                                    THEN&lt;/FONT&gt; named_struct('x', &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt;, 'y', &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt;,&lt;BR /&gt;                                                      'len', &lt;FONT color="#FF9900"&gt;point.x&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.x &lt;/FONT&gt;+ &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;point.y&lt;/FONT&gt;)&lt;BR /&gt;&lt;FONT color="#008000"&gt;                                    ELSE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt; &lt;FONT color="#008000"&gt;END&lt;/FONT&gt;,&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;FONT color="#FF9900"&gt;acc&lt;/FONT&gt; -&amp;gt; struct(&lt;FONT color="#FF9900"&gt;acc.x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;acc.y&lt;/FONT&gt;));&lt;/SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; max_distance(array_agg(struct(&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt;)))&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#008000"&gt;VALUES &lt;/FONT&gt;(1, 10), (2, -10), (-10, 3) AS &lt;FONT color="#FF9900"&gt;points&lt;/FONT&gt;(&lt;FONT color="#FF9900"&gt;x&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;y&lt;/FONT&gt;);&lt;BR /&gt;&lt;FONT color="#993366"&gt;{ x: -10, y: 3 }&lt;/FONT&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;FONT color="#000000"&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt; This isn't the fastest way to perform this particular aggregation, but it serves to illustrate the purpose.&lt;BR /&gt;&lt;BR /&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;H1&gt;&lt;SPAN&gt;What else is there?&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;There are a decent number of higher-order functions that you can use.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;Here is a list as of the writing of the blog:&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/aggregate.html" target="_self"&gt;&lt;SPAN&gt;aggregate(expr,start,merge[,finish])&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Aggregates elements in an array using a custom aggregator.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/array_sort.html" target="_self"&gt;&lt;SPAN&gt;array_sort(array,func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Returns the input array sorted according to provided lambda.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/exists.html" target="_self"&gt;&lt;SPAN&gt;exists(expr, pred)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Returns true if &lt;STRONG&gt;pred&lt;/STRONG&gt; is true for any element in &lt;STRONG&gt;expr&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/filter.html" target="_self"&gt;&lt;SPAN&gt;filter(expr,func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Filters the array in &lt;STRONG&gt;exp&lt;/STRONG&gt;r using the provide lambda.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/forall.html" target="_self"&gt;&lt;SPAN&gt;forall(expr, predFunc)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Tests whether &lt;STRONG&gt;predFunc&lt;/STRONG&gt; holds for all elements in the array.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/reduce.html" target="_self"&gt;&lt;SPAN&gt;reduce(expr,start,merge[,finish])&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Aggregates elements in an array using a custom aggregator.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/transform.html" target="_self"&gt;&lt;SPAN&gt;transform(expr, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Transforms elements in an array in &lt;STRONG&gt;expr&lt;/STRONG&gt; using the function &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/zip_with.html" target="_self"&gt;&lt;SPAN&gt;zip_with(expr1, expr2, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Merges the arrays in &lt;STRONG&gt;expr1&lt;/STRONG&gt; and &lt;STRONG&gt;expr2&lt;/STRONG&gt;, element-wise, into a single array using &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/map_filter.html" target="_self"&gt;&lt;SPAN&gt;map_filter(expr, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Filters entries in the map in &lt;STRONG&gt;expr&lt;/STRONG&gt; using the function &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/map_zip_with.html" target="_self"&gt;&lt;SPAN&gt;map_zip_with(map1, map2, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Merges &lt;STRONG&gt;map1&lt;/STRONG&gt; and &lt;STRONG&gt;map2&lt;/STRONG&gt; into a single map.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/transform_keys.html" target="_self"&gt;&lt;SPAN&gt;transform_keys(expr, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Transforms keys in a map in &lt;STRONG&gt;expr&lt;/STRONG&gt; using the function &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/transform_values.html" target="_self"&gt;&lt;SPAN&gt;transform_values(expr, func)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Transforms values in a map in &lt;STRONG&gt;expr&lt;/STRONG&gt; using the function &lt;STRONG&gt;func&lt;/STRONG&gt;.&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;&lt;SPAN&gt;Conclusion&lt;/SPAN&gt;&lt;/H1&gt;
&lt;P&gt;&lt;SPAN&gt;Higher-order functions using lambda functions are a very powerful, yet under appreciated feature of Spark SQL.&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;They can be used to transform arrays and maps, and perform user-defined aggregations.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;References:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI data-stringify-indent="0" data-stringify-border="0"&gt;&lt;A class="c-link" href="https://www.databricks.com/blog/parameterized-queries-pyspark" target="_blank" rel="noopener noreferrer" data-stringify-link="https://www.databricks.com/blog/parameterized-queries-pyspark" data-sk="tooltip_parent"&gt;Blog: Parameterized queries with PySpark&lt;/A&gt;&lt;/LI&gt;
&lt;LI data-stringify-indent="0" data-stringify-border="0"&gt;&lt;A class="c-link" href="https://www.databricks.com/blog/2018/11/16/introducing-new-built-in-functions-and-higher-order-functions-for-complex-data-types-in-apache-spark.html" target="_blank" rel="noopener noreferrer" data-stringify-link="https://www.databricks.com/blog/2018/11/16/introducing-new-built-in-functions-and-higher-order-functions-for-complex-data-types-in-apache-spark.html" data-sk="tooltip_parent"&gt;Blog: New built in functions and higher order functions in Apache Spark&lt;/A&gt;&lt;/LI&gt;
&lt;LI data-stringify-indent="0" data-stringify-border="0"&gt;&lt;A class="c-link" href="https://www.databricks.com/blog/2017/05/24/working-with-nested-data-using-higher-order-functions-in-sql-on-databricks.html" target="_blank" rel="noopener noreferrer" data-stringify-link="https://www.databricks.com/blog/2017/05/24/working-with-nested-data-using-higher-order-functions-in-sql-on-databricks.html" data-sk="tooltip_parent"&gt;Blog: Working with nested data using higher order functions in Databricks with SQL&lt;/A&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;SQL Ref: Lambda Functions&lt;/SPAN&gt;&lt;/A&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/array_sort.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;SQL Ref: array_sort&lt;/SPAN&gt;&lt;/A&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/reduce.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;SQL Ref: Reduce&lt;/SPAN&gt;&lt;/A&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;A href="https://github.com/srielau/notebooks/blob/main/LAMBDA%20FUNCTION%20BLOG.sql" target="_self"&gt;Notebook with SQL in this post&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;</description>
      <pubDate>Tue, 27 Feb 2024 21:12:04 GMT</pubDate>
      <guid>https://community.databricks.com/t5/technical-blog/lambda-functions-knights-of-the-higher-order/ba-p/61264</guid>
      <dc:creator>SergeRielau</dc:creator>
      <dc:date>2024-02-27T21:12:04Z</dc:date>
    </item>
  </channel>
</rss>

