Skip to content

Commit b60e795

Browse files
authored
feat: Add array_transpose SQL function (#26470)
## Description Adds a new SQL-invoked function `array_transpose` that transposes 2D arrays (matrices). Transforms a 2D array such that `a[x][y]` becomes `transpose(a)[y][x]` ## Motivation and Context Matrix transposition is a common operation in data analysis and transformations. This function provides a convenient SQL builtin for transposing 2D arrays without requiring complex nested queries or external processing. ## Test Plan Test are passing ## Contributor checklist - [x] Please make sure your submission complies with our [contributing guide](https://github.com/prestodb/presto/blob/master/CONTRIBUTING.md), in particular [code style](https://github.com/prestodb/presto/blob/master/CONTRIBUTING.md#code-style) and [commit standards](https://github.com/prestodb/presto/blob/master/CONTRIBUTING.md#commit-standards). - [x] PR description addresses the issue accurately and concisely. If the change is non-trivial, a GitHub Issue is referenced. - [x] Documented new properties (with its default value), SQL syntax, functions, or other functionality. - [x] If release notes are required, they follow the [release notes guidelines](https://github.com/prestodb/presto/wiki/Release-Notes-Guidelines). - [x] Adequate tests were added if applicable. - [ ] CI passed. ## Release Notes Please follow [release notes guidelines](https://github.com/prestodb/presto/wiki/Release-Notes-Guidelines) and fill in the release notes below. ``` == RELEASE NOTES == General Changes * add :func:`array_transpose` to return a transpose of an array.
1 parent 7f2e5c5 commit b60e795

File tree

3 files changed

+37
-0
lines changed

3 files changed

+37
-0
lines changed

presto-docs/src/main/sphinx/functions/array.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,17 @@ Array Functions
258258
SELECT array_top_n(ARRAY [1, 100], 5); -- [100, 1]
259259
SELECT array_top_n(ARRAY ['a', 'zzz', 'zz', 'b', 'g', 'f'], 3); -- ['zzz', 'zz', 'g']
260260

261+
.. function:: array_transpose(array(array(T))) -> array(array(T))
262+
263+
Returns a transpose of a 2D array (matrix), where rows become columns and columns become rows.
264+
Converts ``a[x][y]`` to ``transpose(a)[y][x]``. All rows in the input array must have the same length, otherwise the function will fail with an error.
265+
Returns an empty array if the input is empty or if all rows are empty. ::
266+
267+
SELECT array_transpose(ARRAY [ARRAY [1, 2, 3], ARRAY [4, 5, 6]]) -- [[1, 4], [2, 5], [3, 6]]
268+
SELECT array_transpose(ARRAY [ARRAY ['a', 'b'], ARRAY ['c', 'd'], ARRAY ['e', 'f']]) -- [['a', 'c', 'e'], ['b', 'd', 'f']]
269+
SELECT array_transpose(ARRAY [ARRAY [1]]) -- [[1]]
270+
SELECT array_transpose(ARRAY []) -- []
271+
261272
.. function:: arrays_overlap(x, y) -> boolean
262273

263274
Tests if arrays ``x`` and ``y`` have any non-null elements in common.

presto-main-base/src/test/java/com/facebook/presto/operator/scalar/sql/TestArraySqlFunctions.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,4 +401,17 @@ public void testArrayTopNEdgeAndErrorCase()
401401
assertFunction("ARRAY_TOP_N(ARRAY [], 3)", new ArrayType(UNKNOWN), emptyList());
402402
assertFunction("ARRAY_TOP_N(ARRAY [1, 4], 3)", new ArrayType(INTEGER), ImmutableList.of(4, 1));
403403
}
404+
405+
@Test
406+
public void testArrayTranspose()
407+
{
408+
assertFunction("ARRAY_TRANSPOSE(ARRAY[ARRAY[1, 2, 3], ARRAY[4, 5, 6]])", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of(ImmutableList.of(1, 4), ImmutableList.of(2, 5), ImmutableList.of(3, 6)));
409+
assertFunction("ARRAY_TRANSPOSE(ARRAY[ARRAY[1]])", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of(ImmutableList.of(1)));
410+
assertFunction("ARRAY_TRANSPOSE(ARRAY[])", new ArrayType(new ArrayType(UNKNOWN)), emptyList());
411+
assertFunction("ARRAY_TRANSPOSE(ARRAY[ARRAY[VARCHAR 'a', VARCHAR 'b'], ARRAY[VARCHAR 'c', VARCHAR 'd'], ARRAY[VARCHAR 'e', VARCHAR 'f']])", new ArrayType(new ArrayType(VARCHAR)), ImmutableList.of(ImmutableList.of("a", "c", "e"), ImmutableList.of("b", "d", "f")));
412+
assertFunction("array_transpose(array[array[1, null, 3], array[4, 5, null]])", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of(ImmutableList.of(1, 4), asList(null, 5), asList(3, null)));
413+
assertFunction("array_transpose(array_transpose(array[array[1, 2, 3], array[4, 5, 6]]))", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of(ImmutableList.of(1, 2, 3), ImmutableList.of(4, 5, 6)));
414+
415+
assertInvalidFunction("array_transpose(array[array[1, 2, 3], array[4, 5]])", StandardErrorCode.GENERIC_USER_ERROR, "All rows must have the same length for matrix transpose");
416+
}
404417
}

presto-sql-helpers/presto-sql-invoked-functions-plugin/src/main/java/com/facebook/presto/scalar/sql/ArraySqlFunctions.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,17 @@ public static String arrayTopNComparator()
174174
{
175175
return "RETURN IF(n < 0, fail('Parameter n: ' || cast(n as varchar) || ' to ARRAY_TOP_N is negative'), SLICE(REVERSE(ARRAY_SORT(input, f)), 1, n))";
176176
}
177+
178+
@SqlInvokedScalarFunction(value = "array_transpose", deterministic = true, calledOnNullInput = false)
179+
@Description("Transposes a 2D array (matrix) such that a[x][y] = transpose(a)[y][x]. All rows must have the same length.")
180+
@TypeParameter("T")
181+
@SqlParameter(name = "input", type = "array(array(T))")
182+
@SqlType("array(array(T))")
183+
public static String arrayTranspose()
184+
{
185+
return "RETURN IF(cardinality(input) = 0, input, " +
186+
"IF(any_match(input, row -> cardinality(row) != cardinality(input[1])), " +
187+
"fail('All rows must have the same length for matrix transpose'), " +
188+
"transform(sequence(1, cardinality(input[1])), x -> transform(input, y -> y[x]))))";
189+
}
177190
}

0 commit comments

Comments
 (0)