I have two array values of the same length in PostgreSQL:
{a,b,c}
and {d,e,f}
and I'd like to combine them into
{{a,d},{b,e},{c,f}}
Is there a way to do that?
PostgreSQL ARRAY_AGG() function is an aggregate function that accepts a set of values and returns an array where each value in the input set is assigned to an element of the array. Syntax: ARRAY_AGG(expression [ORDER BY [sort_expression {ASC | DESC}], [...]) The ORDER BY clause is an voluntary clause.
PostgreSQL UNNEST() function This function is used to expand an array to a set of rows.
PostgreSQL allows columns of a table to be defined as variable-length multidimensional arrays. Arrays of any built-in or user-defined base type, enum type, composite type, range type, or domain can be created.
has array_agg(array expression)
:
array_agg
(anyarray
) →anyarray
Concatenates all the input arrays into an array of one higher dimension. (The inputs must all have the same dimensionality, and cannot be empty or null.)
This is a drop-in replacement for my custom aggregate function array_agg_mult()
demonstrated below. It's implemented in C and considerably faster. Use it.
Use the ROWS FROM
construct or the updated unnest()
which takes multiple arrays to unnest in parallel. Each can have a different length. You get (per documentation):
[...] the number of result rows in this case is that of the largest function result, with smaller results padded with null values to match.
Use this cleaner and simpler variant:
SELECT ARRAY[a,b] AS ab FROM unnest('{a,b,c}'::text[] , '{d,e,f}'::text[]) x(a,b);
Consider the following demo for Postgres 9.3 or earlier:
SELECT ARRAY[a,b] AS ab FROM ( SELECT unnest('{a,b,c}'::text[]) AS a , unnest('{d,e,f}'::text[]) AS b ) x;
Result:
ab ------- {a,d} {b,e} {c,f}
Note that both arrays must have the same number of elements to unnest in parallel, or you get a cross join instead.
You can wrap this into a function, if you want to:
CREATE OR REPLACE FUNCTION zip(anyarray, anyarray) RETURNS SETOF anyarray LANGUAGE SQL AS $func$ SELECT ARRAY[a,b] FROM (SELECT unnest($1) AS a, unnest($2) AS b) x; $func$;
Call:
SELECT zip('{a,b,c}'::text[],'{d,e,f}'::text[]);
Same result.
Now, if you want to aggregate that new set of arrays into one 2-dimenstional array, it gets more complicated.
SELECT ARRAY (SELECT ...)
or:
SELECT array_agg(ARRAY[a,b]) AS ab FROM ( SELECT unnest('{a,b,c}'::text[]) AS a ,unnest('{d,e,f}'::text[]) AS b ) x
or:
SELECT array_agg(ARRAY[ARRAY[a,b]]) AS ab FROM ...
will all result in the same error message (tested with pg 9.1.5):
ERROR: could not find array type for data type text[]
But there is a way around this, as we worked out under this closely related question.
Create a custom aggregate function:
CREATE AGGREGATE array_agg_mult (anyarray) ( SFUNC = array_cat , STYPE = anyarray , INITCOND = '{}' );
And use it like this:
SELECT array_agg_mult(ARRAY[ARRAY[a,b]]) AS ab FROM ( SELECT unnest('{a,b,c}'::text[]) AS a , unnest('{d,e,f}'::text[]) AS b ) x
Result:
{{a,d},{b,e},{c,f}}
Note the additional ARRAY[]
layer! Without it and just:
SELECT array_agg_mult(ARRAY[a,b]) AS ab FROM ...
You get:
{a,d,b,e,c,f}
Which may be useful for other purposes.
Roll another function:
CREATE OR REPLACE FUNCTION zip2(anyarray, anyarray) RETURNS SETOF anyarray LANGUAGE SQL AS $func$ SELECT array_agg_mult(ARRAY[ARRAY[a,b]]) FROM (SELECT unnest($1) AS a, unnest($2) AS b) x; $func$;
Call:
SELECT zip2('{a,b,c}'::text[],'{d,e,f}'::text[]); -- or any other array type
Result:
{{a,d},{b,e},{c,f}}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With