# Remove oddly nested substrings

Input will consist of some text, with nested [] separating some of it into nested groups:

abc[d[fgijk]nopq]rst[u[v[[w]]]xy[[[z]]]]


Your task is to remove all brackets, and everything wrapped in an odd number of brackets. For example, a[b]c would remove the [], and the b inside of it. a[[b]]c, however, would only remove the brackets (as b is wrapped in an even number of brackets). If the string were instead a[b[c]d]f, the result would be acf, as the b and d are wrapped in an odd number of brackets, but c is not.

The correct output for the input given at the top would be:

abcfgijkrstvwz


I/O:

You can take input as a string, or any reasonable representation of one (like an array of characters). You can output in a similar fashion, with trailing whitespacing being allowed. You can assume all non-bracket characters are lowercase letters, and you can choose whether the brackets are (), [], or {}.

You can assume the brackets are properly nested (e.g., [ or ][ won't be given as inputs), and that there is at least one letter in the input.

Test cases:

abc                                                 abc
a[b]c                                               ac
[ab]c                                               c
ab[c]                                               ab
[]abc                                               abc
a[]bc                                               abc
abc[]                                               abc
a[[b]]c                                             abc
a[[[b]]]c                                           ac
a[b][]c                                             ac
[a[b]c]                                             b
a[b[c]d]f                                           acf
[ab]c[df]                                           c
a[a]a[[a]]                                          aaa
abc[d[fgijk]nopq]rst[u[v[[w]]]xy[[[z]]]]            abcfgijkrstvwz
zyx[w]v[ut[s]r[[qp][kj]i]gf][d][][][c][b[][a]]      zyxvsia


Other:

This is , so shortest answer (in bytes) per language wins!

• Can we require input to be uppercase instead of lowercase? Sep 1 at 8:20
• @pxeger No, sorry. Sep 1 at 15:01

# Jelly, 10 8 bytes

e€Ø[œpm2


Full program as we use Jelly' smash printing. Uses some tricks from Jonah's J answer, so be sure to upvote that as well!

-2 bytes thanks to Nick Kennedy.

## How it works

e€Ø[œpm2 - Main link. Takes a string S on the left e.g. S = "a[b[c]d]e"
Ø[     - Builtin constant; Yield "[]"
€       - Over each character in S:
e        -   Is it a bracket?                      e.g. [0,1,0,1,0,1,0,1,0]
œp   - Partition S at the truthy indices       e.g. [['a'], ['b'], ['c'], ['d'], ['e']]
m2 - Take every second sublist               e.g. [['a'], ['c'], ['e']]
Smash-print and return                  e.g. 'abc'

• Here’s a two byte saving: e€Ø[œpm2 Sep 1 at 7:44
• @NickKennedy Very nice! Sep 1 at 8:04

# Retina, 7 bytes

\W\w*.



Try it online!

• -7 bytes (50%) thanks to DLosc :)
• 7 bytes (but doesn't work with multiple test cases at once) Sep 1 at 3:34
• @DLosc Ah, I missed the note that I can assume the input only contains [, ] and letters.
– tsh
Sep 1 at 3:38
• [][].*?[][] would have worked for the first approach. To use @DLosc's approach with multiple test cases simply put %(G in the header.
– Neil
Sep 1 at 9:23

# J, 39 24 23 18 15 bytes

#~'_'(*=/\)@I.]


Try it online!

-3 bytes and new approach thanks to FrownyFrog!

## old answer, 39 24 23 18 bytes

(+:~:/\)@e.&'[]'#]


Try it online!

Saved around 10 bytes after reading dingledooper's answer and realizing I didn't have distinguish between open and close brackets.

-6 thanks to some damn clever tricks by Bubbler!

Consider 'a[b[c]d]f':

• e.&'[]' Is the input a bracket? Returns a boolean mask:

0 1 0 1 0 1 0 1 0

• (+:~:/\)@ Transform that mask first by a scan using a not equal reduction. Only items with odd numbers of brackets to their left (including themselves) become 1. Then "not-or" +: that result with the original mask:

1 0 0 0 1 0 0 0 1

• #] Use that to filter the original input:

acf

• 2|+/\~:/\ Sep 1 at 2:06
• 18 Sep 1 at 2:31
• Oh that's really clever. I don't think I've ever used dyadic +: in a golf before.... Sep 1 at 2:36
• Oh my, I was really not expecting any more golfs after that 18. Well done! Sep 9 at 15:30
• It's not really a new approach, I just flipped everything. Not a bracket, equality, "and" instead of "not-or". Sep 10 at 3:29

# Vyxals, 6 bytes

‛\Wṡy$ Try it Online!  ṡ # Split on ‛\W # Non-words y # Uninterleave$ # Get first half


Beating Jelly :) -2 thanks to Aaron Miller.

• What a gamer winning the challenge. Very cool Sep 1 at 3:34
• @exedraj Because of abusing various builtins, I can actually get 6 for uppercase :) Sep 1 at 3:35
• @exedraj Also, DLosc came up with 7 in Retina. Sep 1 at 3:36
• Not any more, @exedraj! :p Sep 1 at 8:46

# Japt v2.0a0 -P, 7 5 bytes

Vyxal was being too cocky for my liking! :D

q\W ë

q\W ë     :Implicit input of string
q         :Split on
\W       :  RegEx /[^a-z0-9]/i
ë     :Get every second element, starting with the first
:Implicitly join and output

• Nooo why would you do this?!? :p Sep 1 at 9:19

# Python 3, 45 bytes

Input is taken from STDIN, and outputs a list of characters, each on its own line.

n=1
for c in input():n^=c<'a'or n>0==print(c)


Try it online!

The main observation is that whether or not a letter is wrapped in an odd number of brackets can be solely determined by taking the parity of the number of brackets preceding that letter.

### Python 3, 48 bytes

Same as above, but outputs the entire string on one line.

n=1
for c in input():n^=not''<c!=print(end=c*n)


Try it online!

### Python 3.8 (pre-release), 49 bytes

A lambda-based solution which is just slightly longer, in comparison:

lambda L,n=1:[c for c in L if(n:=n^(b:=c<'a'))>b]


Try it online!

# R, 33 32 bytes

-1 byte thanks to @Shaggy.

function(s)gsub("\\W\\w*.","",s)


Try it online!

Simple regex-based solution.

• Save a byte by changing the RegEx to "\\W\\w*.". Sep 1 at 9:16
• @Shaggy, thanks! I somehow missed that. Sep 1 at 9:20

# Zsh, 19 bytes

<<<${(e)1//[][]/\} Attempt This Online! Replaces [ and ] with , and (e)xpands the backtick substitutions. For example, A[[]BCD] becomes ABCD  The contents of backticks when in strings are evaluated as commands. Since there are no commands made only of uppercase letters, the bits in backticks do nothing and aren't included in the output. If we can't assume input is uppercase, the same method can be adapted to force it to uppercase: ## Zsh, 26 bytes <<<${(L)${(eU)1//[][]/\}} Attempt This Online! ## Zsh, 21 bytes <<<${1//[][][a-z]#?/}

Attempt This Online!

A less interesting alternative that uses the equivalent of the \W\w*. regex method used by some other answers: [][] matches any square bracket, then [a-z] any letter (# means "0 or more times"), then ? matches any single character.

Must be run with --extendedglob to enable the # pattern feature.

• Great answer. More useful than the zsh manual on the subject of pattern matching Sep 1 at 23:09

# brainfuck, 9689 71 bytes

-25(!) bytes thanks to Daniel Cristofani

Uses () as brackets.

,[[>+>+<<-]+++++[>--------<-]>[-[[-]>>[<]<[.<<]]]>[>-[++>]<[<]]>[-]<<,]


Try it online!

This is the most complex brainfuck I've ever written and I had quite a few troubles with getting the pointer back to a common position after branches, so I suspect there is some room for optimization.

                              tape layout: _; input1; input2; parity flag
,[                            while loop: runs as long as there is input
[>+>+<<-]                   make two copies of the input to the right
+++++[>--------<-]>         subtract 40 from the first copy
[-[                         if the result is not 0 or 1:
[-]                         set first copy to 0
>>[<]<[.<<]                 print second copy is not set
pointer is now at _
]]
>[                          if the pointer is at input1
>-[++>]<[<]                 toggle the parity flag
]
>[-]<<                      set second copy to 0
,]


Try it online!

• +1 I really like the [--[ check for both brackets. Before using my compiler, I had a go at it by hand and gave up needing to constantly copy a flag cell. Of course, using [<] also shows a neater tape layout. Sep 3 at 0:17
• This is a good start. But yeah, it could be shorter. Here's a 71-byte version. The layout here is _; input1; input2; parity, and I use movement rather than an explicit flag to control the "brackets or not" choice also. Also, the problem statement allows accepting parentheses rather than square brackets, and that saves 8. Sep 16 at 5:25
• @DanielCristofani Thanks a lot for your suggestions! I will include once I managed to understand them fully ;)
– ovs
Sep 16 at 10:14
• @ovs Excellent. Let me know if there's anything I can help clarify. Sep 17 at 4:56

# Wolfram Language (Mathematica), 40 bytes

StringSplit[#,"["|"]",All][[;;;;2]]<>""&


Try it online!

• What do all the semicolons do? Sep 1 at 5:33
• @Jonah It's similar to Python's slice, but with ;; instead of : (Span).
– att
Sep 1 at 6:41

# AutoHotKey, 44 bytes

#UseHook
[::
]::
A:=not A
BlockInput,%A%


You need execute it as Administrator permission. And you can always back to normal input status with Ctrl-Alt-Delete if you are in trouble.

# Pip, 10 bytes

@UW:a^\W


Try it online!

### Explanation

The key observation is that the two types of brackets don't need to be distinguished...

    a       First command-line argument
^\W  Split on this regex (any non-word character, i.e. a bracket)
UW:        Unweave (the : is needed for precedence reasons, unfortunately)
@           Take the first of the unweave results


Unweave turns a list like [1 2 3 4 5] into [[1 3 5] [2 4]], so @UW applied to the split results gives every other result, starting with the first.

# JavaScript, 27 26 bytes

Using pajonk's RegEx replacement approach; be sure to +1 them if you're +1ing this.

s=>s.replace(/\W\w*./g,)


Try it online!

## Original, 44 bytes

s=>s.split(/\W/).filter((_,y)=>++y%2).join


Try it online!

# C (gcc), 55 52 bytes

-3 thanks to dingledooper.

Scans through the string and increments a even-starting depth counter, printing any characters on an even depth: as the input is guaranteed to be balanced, the starting depth will always be an even number. Uses Jonah's trick of only scanning for brackets regardless of direction.

l;f(char*s){for(;*s;s++)*s>96?l%2||putchar(*s):l++;}


Try it online!

• 52 bytes Sep 1 at 6:41
• You rely on undefined behavior since l needs to be initially even and f is furthermore not reusable (see codegolf.meta.stackexchange.com/questions/4939/…). Also, you write to stdout yet have not defined an entire program. Sep 3 at 0:25
• @JonathanFrech Functions writing to stdout is allowed 2 days ago
• @JonathanFrech Since the input is always guaranteed to have a balanced number of braces the function is reusable. It only breaks when given an invalid input. 2 days ago

f(h:t)=[h|h>']',even$sum[1|b<-t,b<'a']]++f t f x=x  Try it online! # VyxalK, 24 bytes Thanks to exedraj, emanresu A and hyper-neutrino for guiding me through my Vyxal journey's beginning. ƛ91≠*;ƛ93≠*;'[¥¬|¥¬£];C∑  Try it Online! # JavaScript (Node.js), 46 bytes x=>[...x].filter(x=>(z^=b=x>'z')>b,z=1).join  Try it online! Port of dingledooper's answer thanks to dingledooper. # JavaScript (Node.js), 51 bytes x=>x.replace(/[{}]([^{}]*)/g,(a,b)=>i++%2?b:'',i=0)  Try it online! This makes use of the fact that after an even amount of open or close brackets, the bit of the string up until the next bracket should be kept. After an odd amount, it shouldn't. For example, on the demo input string, the following happens: Full: abc[d[fgijk]nopq]rst[u[v[[w]]]xy[[[z]]]] Valid: abc [fgijk ]rst [v [w ] [ [z ] ] Invalid: [d ]nopq [u [ ] ]xy [ ] ]  We just smoosh the valid half together, remove all the brackets, and we have our answer! • I'm not particularly good at JS, but perhaps this works for 46? Sep 1 at 2:27 • @dingledooper Nice, thanks! Sep 1 at 2:28 # APL (Dyalog Classic), 22 bytes {((~≠\⍵∊'[]')/⍵)~'[]'}  Try it online! Explained { } ⍝ Monadic function, takes string on left ⍵∊'[]') ⍝ Boolean mask for characters which are braces ((~≠\ ⍝ Scan the mask by not-equal and negate (Mask for characters that is needed to kept with braces) /⍵) ⍝ Filter the input using the scanned mask ~'[]' ⍝ Remove the braces  Try it online! # APL (Dyalog Classic), 24 bytes {((~2|+\⍵∊'[]')/⍵)~'[]'}  Try it online! Monadic function, takes the string on right Approaches in array oriented languages are really neat • You can replace the 2|+\  with a scan by XOR (^\  I believe) Sep 1 at 2:52 • @Dudecoinheringaahing doesn't work I think (I have managed to golf 2 more bytes by bubbler new approach) Sep 1 at 3:04 • @Dudecoinheringaahing There's no bitwise XOR built-in. ≠\ does the job for booleans (which is what ~:/\ does in J). Sep 1 at 5:29 • The commute operator ⍨ can be helpful in reducing the number of parenthesis, by moving the simple argument of a function to the left: tio.run/##SyzI0U2pTMzJT9dNzkksLs5M/v@/… – ovs Sep 1 at 8:04 # jq, 19 bytes gsub("\\W\\w*.";"")  Uses the regular expression from tsh's answer. Try it online! • Nice! Can you edit it into this please, and I'll start a bounty in two days when the question's old enough. Sep 1 at 7:57 # Charcoal, 16 bytes ＦＳ¿№βιＦ¬﹪Ｌυ²ι⊞υι  Try it online! Link is to verbose version of code. Explanation: Prints lowercase letters unless an odd number of other characters have been recorded. Alternative solution, also 16 bytes: ⪫Φ⪪⪫⪪Ｓ]¦[¦[¬﹪κ²ω  Try it online! Link is to verbose version of code. Explanation: Replaces ]s with [s, then splits on [s and outputs alternate elements. # Haskell, 66 61 bytes f(x,y)c=(x-sum[x+x|c>'z'],y++[c|c<'{',x>0]) snd.foldl f(1,"")  Try it online! Haskell doesn't really do regex. # Python 3, 40 bytes (using regex) lambda s:re.sub("\W\w*.","",s) import re  Try it online! ## Version without lambda, 44 bytes import re print(re.sub("\W\w*.","",input()))  Try it online! Thanks to @emanresu A for the lambda version • 40 with a lambda Sep 1 at 9:24 • @emanresuA thanks Sep 1 at 15:27 • How does this work with having the import after re is used? I haven't seen this before Sep 2 at 12:19 • @JamesWhiteley It is not important weather the import is made before or after the definition of the lambda function. However, it is more convenient for me to do so because I can put f= in the header in TIO as it isn't counted in the byte count Sep 2 at 13:38 • @JamesWhiteley, re isn't used until the lambda is called, which happens after the import in the TIO. Try moving the import to the end of the footer and it will error as you expect. Sep 3 at 9:12 # K (ngn/k), 12 bytes {x&=\x>:93}#  Try it online! Takes advantage of the input restrictions, i.e. only having to deal with lower cased characters and [] as the brackets. • {...}# set up a filter, removing items from the (implicit) input that do not match the criteria in {...} • x>:93 determine which characters in the input are letters, updating x inplace (ASCII codes for [] are 91 93, a..z are 97..122) • =\ do an equals-scan over this (characters at an even level of nesting become 1, otherwise are 0) • x& take the logical and, essentially asking "is this a letter AND is it at even level of nesting?" # Ly, 37 bytes 1s>ir['aL:[pfpl!sp0]p![pl[p:o0]pp0]p]  Try it online! This code takes advantage of the input constraints that the characters will only come from the set of characters a-z and [ or ]. Which means checking to see if the codepoint is less than the codepoint for a is sufficient to identify the brackets. It also relies on the constraint that only valid sequences of open and close brackets will given. That allows each bracket to be a "on/off" toggle for the print setting, since it doesn't matter whether it's an open or close, it will always change the even/odd state of the accumulated groupings. 1s -- Set "true" as initial print boolean state > -- Change to a clean stack ir -- Read all input as codepoints, reverse stack [ ] -- Loop to process each input codepoint 'aL -- Compare top of stack to "a" :[p 0]p -- if/then, true is codepoint is "[" or "]" fp -- Pull codepoint forward and delete l!sp -- Flip "on/off" boolean print state ![p 0]p -- if/then, true if codepoint is "a-z" l -- Load print boolean state [p 0]p -- if/then, true if we should print the character :o -- Duplicate the codepoint and print one copy p -- Delete the current codepoint from the stack  # brainfuck, 439 bytes ,[>[-]>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]>[-]<<<<[->>>+>+<<<<]>>>>[-<<<<+>>>>]<[-<->][-]>[-]<<[->+>+<<]>>[-<<+>>][-]+<[[-]>[-]<]>[[-]>[-]+<<<<<<[->>>>>>[-]<<<<<<]>>>>>>[-<<<<<<+>>>>>>]<<<<+>>>]<<++>[-]+<[[-]>[-]<]>[[-]>[-]+<<<<<[->>>>>[-]<<<<<]>>>>>[-<<<<<+>>>>>]<<<+>>]<[-]+<[[-]>[-]<]>[[-]>[-]>[-]<<<<<[->>>>+>+<<<<<]>>>>>[-<<<<<+>>>>>][-]+<[[-]>[-]<]>[[-]<<<<.>>>>]<<]<<,]  Try it online! Generated using a self-written yet poorly optimizing brainfuck compiler still under development. Its brainfuck macro assembler source code without the prelude can be seen below. I am certain a carefully layed out tape together with hand-written brainfuck would dramatically improve this byte count. (macro ppcg233884 () ( ; define parity cell (let:nve$parity 0 (
(let:nve $char 0 ( (getc:w$char)
(while:re $char ( ; check if it is a bracket and update parity (let:nve$wasbracket 0 (
(let:nve $bracket 91 ( ; '[' (sub:ra$char $bracket) (ifnot:re$bracket (
(not:a $parity) (inc:a$wasbracket)))
; ']'
(inc:a $bracket) (inc:a$bracket)
(ifnot:ze $bracket ( (not:a$parity)
(inc:a $wasbracket))))) ; only print on even parity when it was not a bracket (ifnot:ze$wasbracket (
(ifnot:re $parity ( (putc:r$char)))))))

(getc:w \$char)))))))))

(macro main () (
(ppcg233884)))

• Oh hey, I'm working on a BF compiler thing too! Sep 2 at 13:33
• @BrowncatPrograms Intriguing. Do you have a (partial) write up so one may look at it? If you are interested in my Brainfuck Compiler Project, I now blogged about it: blog.jfrech.com/248 Sep 4 at 13:09
• I don't have much written about it yet, but it uses an assembly-like syntax. I've got it half working at the moment, I've finished the parser (still working on some optimizations), and just need to get the actual transpilation part written. Sep 4 at 14:46
• @BrowncatPrograms Did you implement jump instructions? I found traditional assembler does not transpile very well to brainfuck since there program text is not embedded in the tape. Sep 5 at 22:11
• No, that's the main place it's different from assembly. Control flow uses if and while instructions, and instead of functions there's just macros. Sep 6 at 0:00

# 05AB1E, 6 bytes

Aм¡ιн˜


I/O as a list of characters. If a nested list output is allowed, the trailing ˜ could be removed.

Explanation:

A       # Push the lowercase alphabet
м      # Remove all these letters from the (implicit) input-list
¡     # Split the (implicit) input-list on the remaining characters ["[","]"]
ι    # Uninterleave this list of lists into two parts
н   # Only leave the first part of sublists
˜  # Flatten this nested list into a single list of characters
# (after which it is output implicitly as result)


(Could also work for mixed cased letters by replacing A with á.)

# V (vim), 19 bytes

Thanks to Aaron Miller for this two bytes shorter version.

äh:s/]/[/g
òf[df[òx


Try it online!

# V (vim), 21 bytes

xPP:s/]/[/g
òf[df[ò0x


Try it online!

## Explanation

xPP       " Duplicate initial character,
"     since f[ would else skip it.
"     If the input is empty, error out.
:s/]/[/g  " Unify all brackets to [.
ò         " As long as necessary,
f[       "     keep everything until even [ and
df[      "     discard everything until odd [.
ò         " End the loop.
0x        " Remove the duplicate character.

• 19 bytes Sep 3 at 14:52
• @AaronMiller Thank you. Sep 4 at 13:05

# C (gcc), 66 63 bytes

f(char*s){for(char*z=s,n=0;*z=*s++;*z-91&&*z-93?n%2||++z:++n);}


Try it online!

# Curry (PAKCS), 55 bytes

f(h:t)=[h|h>']',0<foldl(*)1[-1|b<-t,b<'a']]++f t
f[]=[]


Try it online!

This is based on Lynn's Haskell answer. There are two difficulties adapting it to Curry however. The first is that curry doesn't have falling through so the case f x=x needs to be replaced* with f[]=[] for an extra byte.

The next is that curry doesn't have sum so we need to replace it with some sort of fold. The most efficient way to do this is to actually make a product instead of a sum` since it is way cheaper to check if something is positive than whether it is even.

*: This doesn't actually behave like I would expect. I'm not a curry expert. However in both the expected behavior and the actual behavior it fails so...