Splitting a list by specifying section headers

Here's my suggestion:

mylist = {"[a]", "a", "a", "[b]", "b", "b", "[ c ]", "c", "c"};

Split[mylist, ! StringMatchQ[#2, "[*"] &]

and we get:

{{"[a]", "a", "a"}, {"[b]", "b", "b"}, {"[ c ]", "c", "c"}}

At the risk of being annoying, I will pitch the linked lists again. Here is the code using linked lists:

ClearAll[split];
split[{}] = {};
split[l_List] :=
  Reap[split[{}, Fold[{#2, #1} &, {}, Reverse@l]]][[2, 1]];

split[accum_, {h_, tail : {_?sectionQ, _} | {}}] :=
  split[Sow[Flatten[{accum, h}]]; {}, tail];

split[accum_, {h_, tail_}] := split[{accum, h}, tail];

The function sectionQ has been stolen from the answer of @rm-rf. The usage is

split[mylist]

(* {{[a],a,a},{[b],b,b},{[ c ],c,c}} *)

The advantages I see in using linked lists is that they allow one to produce solutions which are

  • Easily generalizable to more complex problems
  • Straightforward to implement
  • Easy to argue about (in terms of algorithmic complexity etc)

They may not be the fastest though, so may not always be suitable for performance-critical applications.


Here's one method, using a slightly modified example:

mylist = {"[a]", "a", "[b]", "b", "b", "b", "[ c ]", "c", "c"};

pos = Append[Flatten[Position[mylist,
             s_String /; StringMatchQ[s, "[" ~~ ___]]], Length[mylist] + 1]
   {1, 3, 7, 10}

Take[mylist, {#1, #2 - 1}] & @@@ Partition[pos, 2, 1]
   {{"[a]", "a"}, {"[b]", "b", "b", "b"}, {"[ c ]", "c", "c"}}