I'm trying to write a function to find elements ahead from given positions of given elements in a two dimensional array. basically it involves steps of finding all the positions in array for given element and move the positions a few steps back.
I have created a function to return all matches of the element. But need to modify the function to be able to move the index a few positions back from the position of given element.
see details below:
dev=# \d+ test
Table "public.test"
Column | Type | Modifiers | Storage | Stats target | Description
---------+---------+-----------+----------+--------------+-------------
id | integer | | plain | |
actions | text[] | | extended | |
dev=# select * from test;
id | actions
----+-------------------------------------------------------------------------------------------------------
1 | {{a,1,1,1},{b,3,2,3},{c,2,3,4},{d,5,6,7},{e,3,4,5},{f,6,7,8},{g,0,9,0},{a,2,3,4},{k,3,4,5},{a,2,3,7}}
(1 row)
--function to get element of current position
CREATE OR REPLACE FUNCTION find_in_array(needle ANYELEMENT, haystack ANYARRAY,OUT cname text,out val1 numeric,out val2 numeric,out val3 numeric)
RETURNS setof record
AS $$
SELECT $2[i][1],$2[i][2]::numeric,$2[i][3]::numeric,$2[i][4]::numeric
FROM generate_series(array_lower($2,1), array_upper($2,1)) AS i
WHERE $2[i][1] = $1;
$$ LANGUAGE sql STABLE;
dev=# select find_in_array('a',actions) from test;
find_in_array
---------------
(a,1,1,1)
(a,2,3,4)
(a,2,3,7)
(3 rows)
Now, given element a, I'd like to find the elements about 2-3 positions ahead from letter 'a' (if find the element with same letter, skip it). in the example, it would be: (e,3,4,5) (f,6,7,8) (a,2,3,4) --skipped, because it's got same letter a. (g,0,9,0)
based on answer from @Ziggy, I modified the above function to make it move back a few positions. Further from the current code, if there's a match positioned at the first element of the array, I hope to return a row like 'direct',0,0,0
CREATE OR REPLACE FUNCTION find_in_array_move(needle ANYELEMENT, haystack ANYARRAY)
RETURNS TABLE (cname text, val1 numeric, val2 numeric, val3 numeric)
AS $$
SELECT case when i=1 then 'direct' else $2[i-i_move][1] end,
case when i=1 then 0 else $2[i-i_move][2]::numeric end,
case when i=1 then 0 else $2[i-i_move][3]::numeric end,
case when i=1 then 0 else $2[i-i_move][4]::numeric end
FROM generate_series(2, 3) i_move,
generate_series(array_lower($2,1), array_upper($2,1)) AS i
WHERE $2[i][1] = $1
$$ LANGUAGE SQL STABLE;
The result is:
dev=# select find_in_array_move('a',actions) from test;
find_in_array_move
--------------------
(direct,0,0,0)
(direct,0,0,0)
(f,6,7,8)
(e,3,4,5)
(a,2,3,4)
(g,0,9,0)
(,,,)
(,,,)
(,,,)
(,,,)
(10 rows)
the expected result is (note that I cannot do a distinct as I would allow duplicated rows of "non direct" records):
find_in_array_move
--------------------
(direct,0,0,0)
(f,6,7,8)
(e,3,4,5)
(a,2,3,4)
(g,0,9,0)
any suggestion?