Bracket Expansion!

36

2

Your challenge is to expand some brackets in a program's input as shown:

  1. Find a string s between two matching brackets [ and ], with a single digit n after the closing bracket.
  2. Remove the brackets.
  3. Replace s with itself repeated n times. (If n is 0, simply remove s.)
  4. Go to step 1 until there are no more matching brackets in the input.

Additional rules and clarifications:

  • You will take input and give output via any allowed means.
  • A trailing newline in the output is allowed.
  • You only need to handle printable ASCII in the input.
  • You may assume that all brackets match, i.e. you will never receive the input []]]] or [[[[].
  • You may assume that each closing bracket ] has a digit after it.

Test cases:

Input                -> Output
[Foo[Bar]3]2         -> FooBarBarBarFooBarBarBar
[one]1[two]2[three]3 -> onetwotwothreethreethree
[three[two[one]1]2]3 -> threetwoonetwoonethreetwoonetwoonethreetwoonetwoone
[!@#[$%^[&*(]2]2]2   -> !@#$%^&*(&*($%^&*(&*(!@#$%^&*(&*($%^&*(&*(
[[foo bar baz]1]1    -> foo bar baz
[only once]12        -> only once2
[only twice]23456789 -> only twiceonly twice3456789
[remove me!]0        -> 
before [in ]2after   -> before in in after

As this is , the shortest answer in each language wins. Good luck!

MD XF

Posted 2018-01-30T02:16:52.013

Reputation: 11 605

Sandboxed post. – MD XF – 2018-01-30T02:24:04.867

13You should post another challenge to compress a string back down to its shortest format – Jo King – 2018-01-30T09:11:31.360

Is it worth stating explicitly that your string s should never contain other brackets? For example, attempting to solve [Foo[Bar]3]2 by expanding the string Foo[Bar 3 times would result in an invalid state Foo[BarFoo[BarFoo[Bar]2 – BradC – 2018-01-30T20:17:44.513

@BradC that all depends on how you choose to implement the task. – MD XF – 2018-01-30T21:59:43.090

Does that mean that there are two valid answers to [a[b]2c[d]2e]2? You get abbcddeabbcdde by expanding b and d first, but ababcdbcdedbabcdbcdede by expanding a[b and d]2e first. – BradC – 2018-01-30T22:23:00.567

@BradC Ah. Clarified that they should be matching brackets. – MD XF – 2018-01-30T22:27:36.383

Simpler example: [[a]2[b]2]2 expands to either aabbaabb or aababbaababb depending on how exactly you pair up the brackets. This is an anomaly, though, for most other test cases, you end up with mismatched pairs. Not sure that "matching" entirely conveys the distinction here, but I get the point. Thanks. – BradC – 2018-01-30T22:36:20.497

Suggested test case: [']3 => ''' – caird coinheringaahing – 2018-02-02T16:35:46.387

Answers

13

Gema, 17 characters

[#]?=@repeat{?;#}

Sample run:

bash-4.4$ gema '[#]?=@repeat{?;#}' <<< '[three[two[one]1]2]3'
threetwoonetwoonethreetwoonetwoonethreetwoonetwoone

manatwork

Posted 2018-01-30T02:16:52.013

Reputation: 17 865

Wow, talk about finding the right language for the job! – MD XF – 2018-01-31T02:00:23.723

Or the right job for the language. Many challenges had to skip because the recursive argument was not flexible enough. – manatwork – 2018-01-31T08:57:00.860

Accepting this for now cause I don't see any way it's getting beat, but it'll be unaccepted in the unlikely case that it does. – MD XF – 2018-02-02T03:59:59.520

8

Retina, 24 23 22 bytes

+`\[([^][]*)](.)
$2*$1

Try it online! This is practically a builtin in Retina 1. Edit: Saved 1 byte thanks to @Kobi. 47 45 bytes in Retina 0.8.2:

].
]$&$*¶
{+`\[([^][]*)]¶
$1[$1]
\[([^][]*)]

Try it online!

Neil

Posted 2018-01-30T02:16:52.013

Reputation: 95 035

7

Haskell, 101 96 bytes

fst.(""%)
infix 4%
s%']':d:r=(['1'..d]>>s,r)
s%'[':r|(t,q)<-""%r=s++t%q
s%x:r=s++[x]%r
s%e=(s,e)

Try it online! Instead of using regular expression like most of the other answers, this implements a recursive parser.

-5 bytes thanks to BMO!

Laikoni

Posted 2018-01-30T02:16:52.013

Reputation: 23 676

4

A fixity declaration for (%) saves you 1 byte and ['1'..d] saves you another 4, see this.

– ბიმო – 2018-01-30T08:12:30.080

3@BMO Nice, I didn't expect a fixity declaration would ever be useful for code golfing. I think you should add that to the tips question. – Laikoni – 2018-01-30T08:39:09.330

7

Perl 5, 34 33 29 + 1 (-p) = 30 bytes

s/.([^[]*?)](.)/$1x$2/e&&redo

Try it online!

Cut it down with some help from @Shaggy and @TonHospel.

Xcali

Posted 2018-01-30T02:16:52.013

Reputation: 7 671

3I don't know pearl but redo seems gorgeous! – officialaimm – 2018-01-30T04:02:30.067

I think you should be able to save a byte by not escaping the ]. – Shaggy – 2018-01-30T11:05:37.377

1

I don't know Perl but this seems to work for 30+1 bytes.

– Shaggy – 2018-01-30T11:12:47.713

2These 29+1 also work: perl -pe 's/.([^[]*?)](.)/$1x$2/e&&redo' and perl -pe 's/.([^][]*)](.)/$1x$2/e&&redo' – Ton Hospel – 2018-01-30T15:07:44.240

5

Japt v2, 21 20 19 bytes

Saved 2 bytes thanks to @Shaggy

e/.([^[]*?)](./@YpZ

Test it online!

e is recursive replace, which makes one replacement at a time until there are no more matches. In this case, matches of the regex /\[([^[]*?)](\d)/g are replaced with with <inner text> repeated <digit> times until there are no more matches.

According to what I have planned (here), this regex should eventually be at least 3 2 bytes shorter:

‹[“⁽[»₋”]“.›

ETHproductions

Posted 2018-01-30T02:16:52.013

Reputation: 47 880

2As we "may assume that each closing bracket ] has a digit after it" you should be able to replace (\d with (.. – Shaggy – 2018-01-30T08:19:14.593

You can also replace \[ with . – Shaggy – 2018-01-30T19:27:18.300

@Shaggy Nice, thanks! – ETHproductions – 2018-01-30T23:05:16.453

4

JavaScript, 71 67 66 bytes

I had a 54 byte solution but it got screwed over by the second test case! :(

f=s=>s!=(x=s.replace(/.([^[]*?)](.)/,(_,y,z)=>y.repeat(z)))?f(x):x

Test Cases

f=s=>s!=(x=s.replace(/.([^[]*?)](.)/,(_,y,z)=>y.repeat(z)))?f(x):x
o.innerText=`[Foo[Bar]3]2
[one]1[two]2[three]3
[three[two[one]1]2]3
[!@#[$%^[&*(]2]2]2
[[foo bar baz]1]1
[only once]12
[only twice]23456789
[remove me!]0
before [in ]2after`.split`\n`.map(x=>x.padEnd(22)+`:  `+f(x)).join`\n`
<pre id=o></pre>

Shaggy

Posted 2018-01-30T02:16:52.013

Reputation: 24 623

4

Python 3, 110 93 92 bytes

import re
f=lambda s:f(re.sub(r'\[([^][]+)\](.)',lambda m:m[1]*int(m[2]),s))if'['in s else s

Try it online!

-17 bytes thanks to pizzapants184 -1 byte thanks to Kevin Cruijssen

Gábor Fekete

Posted 2018-01-30T02:16:52.013

Reputation: 2 809

1-17 bytes in Python 3 with re.match indexing and substring check using in. – pizzapants184 – 2018-01-30T18:12:55.870

1-1 byte by changing (\d) to (.), because we know a block-bracket ] is always followed by a digit. – Kevin Cruijssen – 2018-02-01T12:28:56.370

4

Scala, 173 bytes

l.foreach{x=>def r(c:String):String={val t="""\[([^\[\]]*)\](.)""".r.unanchored;c match{case t(g,h)=>r(c.replaceAllLiterally(s"[$g]$h",g*h.toInt));case _=>c}};println(r(x))}

Try it online!

Expanded:

l.foreach { x =>
  def remove(current: String): String = {
    val test ="""\[([^\[\]]*)\](.)""".r.unanchored
    current match {
      case test(g, h) => remove(current.replaceAllLiterally(s"[$g]$h", g * h.toInt))
      case _ => current
    }
  }

  println(remove(x))
}

Old solution

Scala, 219 215 213 212 199 bytes

l.foreach{x=>def r(c:String):String={"""\[([^\[\]]*)\](.)""".r.findFirstMatchIn(c).map{x=>val g=x.group(1);val h=x.group(2).toInt;r(c.replaceAllLiterally(s"[$g]$h",g*h))}.getOrElse(c)};println(r(x))}

Try it online!

Expanded:

l.foreach { x =>
  def remove(current: String): String = {
    """\[([^\[\]]*)\](.)""".r.findFirstMatchIn(current).map { x =>
      val g = x.group(1)
      val h = x.group(2).toInt
      remove(current.replaceAllLiterally(s"[$g]$h", g * h))
    }.getOrElse(current)
  }
  println(remove(x))
}

Where l is the list of strings that we will process.

Thanks Kevin Cruijssen for -1 byte

Went from 212 to 199 by removing an unused parameter, didn't pay attention.

Shikkou

Posted 2018-01-30T02:16:52.013

Reputation: 161

4

Welcome to PPCG! Try tio's scala interpreter at https://tio.run/#scala and see if you can submit a link for the answer, so that others can try it online. :)

– officialaimm – 2018-01-31T09:16:49.093

2Thank you! I edited the answer to include the link. Hopefully it is ok how the header, code and footer is declared in order to be a proper submission. – Shikkou – 2018-01-31T10:17:04.297

1Hi, welcome to PPCG! Great first answer, +1 from me. I think you can save 1 byte by changing (\d) to (.), because we know a block-bracket ] is always followed by a digit. – Kevin Cruijssen – 2018-02-01T12:26:09.987

3

Stacked, 39 38 bytes

Saved 1 byte thanks to Shaggy, golfed the regex!

['\[([^[\]]+)](.)'{.y x:x#~y*}recrepl]

Try it online!

Simply recursively replaces a regex '\[([^[\]]+)](.)' with the repetition rule.

Conor O'Brien

Posted 2018-01-30T02:16:52.013

Reputation: 36 228

I think you can save a byte by not escaping the last ]. – Shaggy – 2018-01-30T11:15:29.980

3

JavaScript - 77 75 72 bytes

f=a=>a.replace(/(.*)\[([^[]*?)](.)(.*)/,(a,b,c,d,e)=>f(b+c.repeat(d)+e))

Edit: updated regex with Shaggy's recommendation

Snippet:

const test = ["[Foo[Bar]3]2", "[one]1[two]2[three]3", "[three[two[one]1]2]3", "[!@#[$%^[&*(]2]2]2", "[[foo bar baz]1]1", "[only once]12", "[only twice]23456789", "[remove me!]0", "before [in ]2after"];

const f=a=>a.replace(/(.*)\[([^[]*?)](.)(.*)/,(a,b,c,d,e)=>f(b+c.repeat(d)+e))

d.innerHTML=test.map(f).join("<br>");
<p id="d">

max890

Posted 2018-01-30T02:16:52.013

Reputation: 131

2

Welcome to PPCG! You can get this down to 70 bytes by tweaking your RegEx.

– Shaggy – 2018-01-31T10:21:05.090

Yes, 72 bytes, obviously, sorry; I was forgetting to count the f=! – Shaggy – 2018-02-01T17:40:47.433

3

Python 3, 155 148 101 97 bytes

def f(x):
 a=x.rfind('[')
 if~a:b=x.find(']',a);x=f(x[:a]+x[a+1:b]*int(x[b+1])+x[b+2:])
 return x

Try It Online

Thanks to HyperNeutrino and Mego for -47 bytes and user202729 for -4 bytes.

Manish Kundu

Posted 2018-01-30T02:16:52.013

Reputation: 1 947

Make it a one-liner to save a couple bytes: def f(x):a=x.rfind('[');b=x.find(']',a);return f(x[:a]+x[a+1:b]*int(x[b+1])+x[b+2:])if~a else x – mathmandan – 2018-01-31T23:01:57.117

2

Java 8, 250 249 241 239 bytes

s->{for(;s.contains("[");)for(int i=0,j,k;i<s.length();)if(s.charAt(i++)==93){String t="",r=t;for(j=k=s.charAt(i)-48;j-->0;)t+=s.replaceAll(r="(.*)\\[([^\\]]+)\\]"+k+"(.*)","$2");s=k<1?t:s.replaceFirst(r,"$1$3").replace("",t);}return s;}

-2 bytes thanks to @JonathanFrech (code contains two unprintable ASCII characters now, which can be seen in the TIO-link below).

Sigh... Java with regex is so damn limited.. I'll just quote myself from another answer here:

Replacing WWWW with 222W is easy in Java, but with 4W not.. If only Java had a way to use the regex capture-group for something.. Getting the length with "$1".length(), replacing the match itself with "$1".replace(...), converting the match to an integer with new Integer("$1"), or using something similar as Retina (i.e. s.replaceAll("(?=(.)\\1)(\\1)+","$#2$1")) or JavaScript (i.e. s.replaceAll("(.)\\1+",m->m.length()+m.charAt(0))) would be my number 1 thing I'd like to see in Java in the future to benefit codegolfing.. >.> I think this is the 10th+ time I hate Java can't do anything with the capture-group match..
Quote from here.

Explanation:

Try it online.

s->{                           // Method with String as both parameter and return-type
  for(;s.contains("[");)       //  Loop as long as the String contains a block-bracket
    for(int i=0,j,k;i<s.length();)
                               //   Inner loop over the characters of the String
      if(s.charAt(i++)==93){   //    If the current character is a closing block-bracket:
        String t="",r=t;       //     Create two temp-Strings, starting empty
        for(j=k=s.charAt(i)-48;//     Take the digit after the closing bracket
            j-->0;)            //     Loop that many times:
          t+=s.replaceAll(r="(.*)\\[([^\\]]+)\\]"+k+"(.*)","$2");
                               //      Append `t` with the word inside the block brackets
        s=k<1?                 //     If the digit was 0:
           t                   //      Replace the input with an empty String as well
          :                    //     Else:
           s.replaceFirst(r,"$1$3").replace("",t);}
                               //      Replace the word between brackets by `t`,
                               //      and remove the digit
  return s;}                   //  Return the modified input-String as result

Kevin Cruijssen

Posted 2018-01-30T02:16:52.013

Reputation: 67 575

1

I think you can use a truly ASCII though unprintable character to save two bytes. (Your solution really takes 241 bytes, 239 characters.)

– Jonathan Frech – 2018-01-30T18:54:47.943

@JonathanFrech Thanks! Was looking for a 1-byte character outside the printable ASCII range. Didn't thought about using an unprintable.. – Kevin Cruijssen – 2018-01-31T07:54:46.317

2

QuadR with the argument, 30 28 bytes

\[[^[]+?].
∊(⍎⊃⌽⍵M)⍴⊂1↓¯2↓⍵M

Try it online!

\[[^[]+?]. replace "[non-[ stuff]character" with

¯2↓⍵M drop the last two characters of the Match ("]digit")
1↓ drop the first character ("[")
 enclose to be treated as a whole
()⍴reshape to length:
⌽⍵M reverse the Match
 pick the first (the digit)
 evaluate
ϵnlist (flatten)

 repeat until no more changes happen


The equivalent Dyalog APL function is 47 bytes:

'\[[^[]+?].'⎕R{∊(⍎⊃⌽⍵.Match)⍴⊂1↓¯2↓⍵.Match}⍣≡

Try it online!

Adám

Posted 2018-01-30T02:16:52.013

Reputation: 37 779

2

C, 407 368 bytes

Thanks to Jonathan Frech for saving bytes.

golfed (file bracket.c):

i,j,k,l,n;char*f(a,m)char*a;{for(i=0;a[i];++i){a[i]==91&&(j=i+1);if(a[i]==93){k=a[i+1]-48;if(!k){for(l=i+2;l<m;)a[++l-i+j-4]=a[l];a=realloc(a,m-3);return f(a,m-3);}for(l=j;l<i;)a[~-l++]=a[l];for(l=i+2;l<m;)a[++l-4]=a[l];m-=3;n=m+~-k*(i---j--);a=realloc(a,n);for(l=i;l<m;)a[l+++~-k*(i-j)]=a[l];for(m=0;m<k;++m)for(l=j;l<i;)a[l+++m*(i-j)]=a[l];return f(a,n);}}return a;}

ungolfed with program:

#include <stdlib.h>
#include <stdio.h>

// '[' = 133
// ']' = 135
// '0' = 48

i, j, k, l, n;

char* f(a,m) char*a;
{
  for (i=0; a[i]; ++i) {
    a[i]==91&&(j=i+1);

    if (a[i]==93) {
      k=a[i+1]-48;

      if (!k) {
        for (l=i+2; l<m; )
          a[++l-i+j-4] = a[l];

        a = realloc(a,m-3);
        return f(a,m-3);
      }
      for (l=j;l<i;)
        a[~-l++] = a[l];
      for (l=i+2; l<m; )
        a[++l-4] = a[l];
      m -= 3;
      n = m+~-k*(i---j--);
      a = realloc(a,n);

      for (l=i; l<m; )
        a[l+++~-k*(i-j)] = a[l];
      for (m=0; m<k; ++m)
        for (l=j; l<i;)
          a[l+++m*(i-j)] = a[l];

      return f(a,n);
    }
  }
  return a;
}

int main()
{
  char c[]="[Foo[Bar]3]2";
  char *b;

  char cc[]="[remove me!]0";
  char *bb;

  char ccc[]="[only once]12";
  char *bbb;

  b=malloc(13);
  bb=malloc(14);
  bbb=malloc(14);

  for (i=0; i<13; ++i)
    b[i] = c[i];

  for (i=0; i<14; ++i)
    bb[i] = cc[i];

  for (i=0; i<14; ++i)
    bbb[i]=ccc[i];

  printf("%s\n", f(b, 13));
  printf("%s\n", f(bb, 14));
  printf("%s\n", f(bbb, 14));

  return 0;
}

Compiled with gcc 5.4.1, gcc bracket.c

Tsathoggua

Posted 2018-01-30T02:16:52.013

Reputation: 131

1368 bytes. – Jonathan Frech – 2018-01-31T18:16:20.200

387 with the needed include (for realloc). I will do a clean update (with the ungolfed version) later. Thanks – Tsathoggua – 2018-02-01T06:54:38.173

If you use GCC, I think the compiler will attempt to guess the definition of both malloc and realloc, including stdlib.h by its own. – Jonathan Frech – 2018-02-01T12:06:05.290

I didn't know that. Nice feature for code golfing. Thanks. – Tsathoggua – 2018-02-02T07:12:15.803

2

Red, 147 bytes

f: func[t][a: charset[not"[]"]while[parse t[any a some[remove["["copy h any a"]"copy d a](insert/dup v: copy""h to-integer d)insert v | skip]]][]t]

Ungolfed:

f: func [t][
    a: charset [not "[]"]                          ; all chars except [ and ]
    while [ parse t [                              ; repeat while parse is returning true
        any a                                      ; 0 or more chars other than [ and ]
        some [                                     ; one or more block:
            remove ["[" copy h any a "]" copy d a] ; remove the entire block, store the
                                                   ; substring between the [] in h,
                                                   ; the digit into d
            (insert/dup v: copy "" h to-integer d) ; makes d copies of h 
            insert v                               ; and inserts them in place 
            | skip ]                               ; skip if no match
        ]                                       
    ][]                                            ; empty block for 'while'
    t                                              ; return the modified string
]

I started learning Red's Parse dialect only yesterday, so I'm sure my code can be improved further. Parse is incomparably more verbose than regex, but is very clear, flexible and readable and can be freely mixed with the rest of the Red language.

Try it online!

Galen Ivanov

Posted 2018-01-30T02:16:52.013

Reputation: 13 815

2

Wolfram Language (Mathematica), 86 bytes

#//.s_:>StringReplace[s,"["~~x:Except["["|"]"]...~~"]"~~d_:>""<>x~Table~FromDigits@d]&

Try it online!

alephalpha

Posted 2018-01-30T02:16:52.013

Reputation: 23 988

1

Jelly, 30 bytes

œṡ”]µḢUœṡ”[ẋ€1¦Ṫ©Ḣ$FṚ;®
Çċ”]$¡

Try it online!


Explanation.


œṡ”]µḢUœṡ”[ẋ€1¦Ṫ©Ḣ$FṚ;®    Helper link 1, expand once.
                           Assume input = "ab[cd]2ef".

œṡ      Split at first occurence of
  ”]      character "]".
    µ   Start new monadic chain. Value = "ab[cd","2ef".

Ḣ       ead. "ab[cd"
 U      Upend. "dc[ba"
  œṡ”[  Split at first occurence of "[". | "dc","ba".

ẋ€        Repeat ...
  1¦        the element at index 1...
          by ...
    Ṫ Ḣ$    the ead of the ail of ...
          the input list ("ab[cd","2ef") (that is, 2)

          The command  also pop the head '2'. The remaining
            part of the tail is "ef".
     ©    Meanwhile, store the tail ("ef") to the register.

          Current value: "dcdc","ba"
FṚ        Flatten and everse. | "abcdcd"
  ;®      Concatenate with the value of the register. "abcdcdef"

Çċ”]$¡    Main link.

 ċ”]$     Count number of "]" in the input.
     ¡    Repeatedly apply...
Ç           the last link...
            that many times.

user202729

Posted 2018-01-30T02:16:52.013

Reputation: 14 620

1

C,381 bytes

Compact version:

while(1){int t=strlen(i);int a,c=-1;char*w;char*s;char*f;while(c++<t){if(i[c]==']'){int k=c-a;w=calloc((k--),1);memcpy(w,&i[a+1],k);s=calloc((t-c-1),1);memcpy(s,&i[c+2],t-c-2);i[a]=0;int r=i[c+1]-48;if(r==0){f=calloc(t,1);sprintf(f,"%s%s",i,s);}else{f=calloc((t+k),1);sprintf(f,"%s%s[%s]%d%s",i,w,w,r-1,s);}free(i);i=f;break;}else if(i[c]=='[')a=c;}free(w);free(s);if(c>=t)break;}

Full version:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void proceed(char* input)
{
  while(1)
  {
    int t=strlen(input);
    int start,cursor=-1;
    char* word;
    char* suffix;
    char* final;
    while(cursor++<t)
    {
      if(input[cursor]==']')
      {
        int wordlength = cursor-start;
        word=calloc((wordlength--),sizeof(char));
        memcpy(word, &input[start+1], wordlength );
        suffix=calloc((t-cursor-1),sizeof(char));
        memcpy( suffix, &input[cursor+2], t-cursor-2 );
        input[start]='\0';
        int rep=input[cursor+1]-'0';
        if(rep==0)
        {
          final=calloc(t,sizeof(char));
          sprintf(final,"%s%s",input,suffix);
        }
        else
        {
          final=calloc((t+wordlength+5),sizeof(char));
          sprintf(final,"%s%s[%s]%d%s",input,word,word,rep-1,suffix);
        }
        free(input);
        input=final;
        break;
      }
      else if(input[cursor]=='[')
        start=cursor;
    }
    free(word);
    free(suffix);

    if(cursor>=t)break;
  }
}

int main()
{
  char* input=calloc(256,sizeof(char));
  sprintf(input,"a[[toto]2b]2[ana]3");
  printf("in : %s\n",input);
  proceed(input);
  printf("out: %s\n",input);
  return 0;
}

raphchar

Posted 2018-01-30T02:16:52.013

Reputation: 11

3Welcome to PPCG! – Shaggy – 2018-01-30T16:57:25.097

1Welcome to the site! Note that C submissions need to be full programs or functions, not just snippets. – MD XF – 2018-01-30T22:12:29.857

1

Python, 80 bytes

import re
b=re.sub
s=lambda x:eval(b(r"\](.)",r"')*\1+'",b(r"\[","'+('","%r"%x)))

Try it online!

s("[Foo[Bar]3]2") Converts [Foo[Bar]3]2 to ''+('Foo'+('Bar')*3+'')*2+'', and evaluates.

Fails for input with quotes in brackets (e.g. [']3)

ugoren

Posted 2018-01-30T02:16:52.013

Reputation: 16 527

I downvoted this answer as the question requires handling any printable ASCII in the input, and this answer doesn't. Notify me if you fix it, and I'll happily retract my vote. – caird coinheringaahing – 2018-02-02T16:32:24.310