# Split along groups

You are going to be given a string containing some alphabetic characters along with [ and ]. Your task is to split into sections that are enclosed in a "group" created by [...] and those that are not. e.g.

"absbn[mesl]meslo[eyyis]me"
->
"absbn"
"mesl"
"meslo"
"eyyis"
"me"


Super simple right? Well hold your horses. What happens when multiple brackets conflict? like co[[x]t or m[[e]]it? We don't have any guarantee that the brackets are matched or not nested.

The rule we are going to use is that when multiple possible groups could be made we will always choose the smallest ones. Any [s left without a match are just regular characters and appear in the output.

So in co[[x]t we could do co [x t, but we could also do co[ x t. Since the group here is either [x or x we choose the smaller one and the result is co[ x t. In the second case me[[e]]it there are 4 ways to make groups here, but unambiguously me[ e ]it results in the smallest group.

Your task will be to take a non-empty string of characters a through z plus two brackets of your choice ([], {}, () and <>) and to split it as described above, providing a list of strings as output.

In cases where a group is on the boundary of the string, e.g. aaa[b] or [ems]ee you may choose to include or omit an empty string "" on that boundary. The test cases always include them.

This is . Answers will be scored in bytes with the goal being to minimize the size of your source code.

## Test cases

go[cat]me -> "go" "cat" "me"
absbn[mesl]meslo[eyyis]me -> "absbn" "mesl" "meslo" "eyyis" "me"
co[[x]t -> "co[" "x" "t"
me[[e]]t -> "me[" "e" "]t"
mon[g]]u -> "mon" "g" "]u"
msy]mesl -> "msy]mesl"
eoa[m -> "eoa[m"
a[b[ -> "a[b["
mesi]mmp[mo -> "mesi]mmp[mo"
meu[ems[mela[] -> "meu[ems[mela" "" ""
w[[[] -> "w[[" "" ""
[foo]foobar[bar] -> "" "foo" "foobar" "bar" ""
[][] -> "" "" "" "" ""

• Please tell me there's a programming language that has this as a part of its syntax… (Or maybe rather don't?) Jan 17, 2022 at 22:33

# Retina 0.8.2, 9 bytes

S<(\w*)>


Try it online! Link includes test suite that switches between [] and <> and separates the output lines for each case with - for convenience. Explanation: Simply splits on a minimal group, capturing the contents.

The question now requires empty chunks, but the previous 10-byte version only required an extra _ prefix to disable them:

_S<(\w*)>


Try it online! Link includes test suite that switches between [] and <> and separates the output lines for each case with - for convenience. Explanation: Splits on a minimal group, capturing the contents even if it is empty, but skipping empty chunks.

# R, 6863 62 bytes

Edit: -1 byte thanks to pajonk

function(x,[=gsub)"@"["",scan(t="<(\\w*)>"[" @\\1 ",x],,"")]


Try it online!

Would be 55 bytes if it wasn't for the annoying behaviour of R's strsplit function, which outputs an extra empty string if the split pattern occurs at the start of the string-to-be-split.

We first search for bracket-pairs (using < and > as brackets) that contain only letters between them, and swap these brackets for spaces. Then, we use scan to split the resulting string (because of the annoying strsplit behaviour). Scan has it's own problem, though, which is that it treats consecutive whitespace as a single delimiter, and so would miss any empty groups: so, we add a spurious @ character to each group, and then remove it with gsub when we're done splitting.

• @pajonk - Aargh! Yes - thanks! As you've probably realized from the edit history, this felt to me more like a battle against gsub, strsplit and scan than against the challenge itself... Jan 16, 2022 at 9:18

# sed, 92 21 20 bytes

s/<$$\w*$$>/\
\1\
/g


Try it online!

Thanks to DLosc for pointing out the specs had changed, greatly simplifying my answer, and for a -3 bytes golf

-1 thanks to "emanresu A" for pointing out I could use <> as my bracket chars

Prints each group on its own line.

• Finds all matches of the form [<string of chars without brackets>].
• Replaces them with \n<string of chars without brackets>\n
• The OP has updated the specification, which I believe means you don't have to special-case anything anymore. Also, \w should work in place of [^][]. Jan 17, 2022 at 17:22
• That makes things much nicer, thanks! Jan 17, 2022 at 17:40
• -2 if you use <> which don't require escaping (I think?) Jan 18, 2022 at 2:26
• @emanresuA Thanks, I missed that we could do that. Jan 18, 2022 at 2:43

# Python3, 158 bytes:

import re
def f(l):
t=''
while l:
if(j:=re.findall('^$\w+$|^',l)):yield t;yield j[0][1:-1];l=l[len(j[0]):];t=''
else:t+=l[0];l=l[1:]
if t:yield t


Try it online!

• You don't need to quote ]s in regexes.
– Neil
Jan 15, 2022 at 18:45

# Pip-p, 11 bytes

a^<(\w*)>


Uses <> for brackets. Attempt This Online!

Verify all test cases

### Explanation

Same idea as Neil's Retina answer: the smallest possible group is always one that does not include any brackets.

a            ; Command-line input
^           ; Split
         ; on this regex:
<         ;   Opening bracket
(   )    ;   Group the following part so it is included in the list of results:
\w*     ;     0 or more word characters (here, letters)
>   ;   Closing bracket


s=>s.replace(/<(\w*)>/g,'0$10').split(0)  Uses <> as the brackets. First time code golfing, yay! I'm pretty sure there's some regex magic which will beat this, but this was the best I could come up with. EDIT: First version didn't properly handle strings starting or ending with groups, e.g. <a>b<c> EDIT2: Shaved off 10 bytes thanks to emanresu A EDIT3: Figured out the regex magic • Welcome to the site! I don't golf in JS, but you might be able to find some useful tips from those who do on our tips for golfing in JS question. Jan 15, 2022 at 17:17 • Array.from(x) can be [...x], and you can save a few more with 0 instead of _. Jan 16, 2022 at 5:30 # Python, 83 bytes lambda s:[t for i,t in enumerate(re.split(r"$([^][]*)$",s))if(i&1)or t] import re Attempt This Online! This uses re.split which returns a list containing both the stuff between matches and those parts of matches that are in grouping parentheses. This is then filtered to remove empty strings but only between groups; empty groups are retained. # Perl 5, 57 bytes s/<(\w*)>/$1 /g;s/^ (.*) $/$1/;print for split/ +/,\$_,-1


Try it online!

# JavaScript, 21 bytes

s=>s.split(/<(\w*)>/)


Try it online!

# Raku, 28 bytes

~«*.split(/$<(\w*)>$/,:v)


No TIO link, because the version of Raku/Perl 6 found there appears to have a regex bug. All of the test cases work fine on my local system though.

• *.split(/.../, :v) splits the input parameter on the given regex. The :v adverb causes the delimiters to be included in the returned list as well, as Match objects.
• ~« converts everything in the split return list to strings. The nondelimiter groups are already strings, but the delimiter Match objects need to be converted.
• $ and $ match the literal square bracket delimiters.
• <( and )> cause the delimiter Match objects to only reference the enclosed text; that is, the square brackets are excluded.
• \w* is a sequence of letters.

All of the allowed delimiter pairs are Raku regex metacharacters, so there's no avoiding having to escape them.