Multi Replace plpgsql

From PostgreSQL wiki
Jump to: navigation, search

Library Snippets

Replace multiple strings in a single pass

Works with PostgreSQL

Any version

Written in

PL/PgSQL

Depends on

Nothing

/* This function quotes characters that may be interpreted as special in a regular expression.
   It's used by the function below and declared separately for clarity. */
CREATE FUNCTION quote_meta(text) RETURNS text AS $$
  select regexp_replace($1, '([\[\]\\\^\$\.\|\?\*\+\(\)])', '\\\1', 'g');
$$ language sql strict immutable;

/* Substitute a set of substrings within a larger string.
   When several strings match, the longest wins.
   Similar to php's strtr(string $str, array $replace_pairs).
   Example:
   select multi_replace('foo and bar is not foobar',
             '{"bar":"foo", "foo":"bar", "foobar":"foobar"}'::jsonb);
   => 'bar and foo is not foobar'
 */
CREATE FUNCTION multi_replace(str text, substitutions jsonb)
RETURNS text
as $$
DECLARE
 rx text;
 s_left text;
 s_tail text;
 res text:='';
BEGIN
 select string_agg(quote_meta(term), '|' )
 from jsonb_object_keys(substitutions) as x(term)
   where term <> ''
 into rx;

 if (coalesce(rx, '') = '') then
   -- the loop on the RE can't work with an empty alternation
   return str;
 end if;

 rx := concat('^(.*?)(', rx, ')(.*)$'); -- match no more than 1 row   

 loop
   s_tail := str;
   select 
       concat(matches[1], substitutions->>matches[2]),
       matches[3]
    from
      regexp_matches(str, rx, 'g') as matches
    into s_left, str;
    
   exit when s_left is null;
   res := res || s_left;

 end loop;

 res := res || s_tail;
 return res;

END 
$$ LANGUAGE plpgsql strict immutable;