Refinement of list manipulation question
This code simply splits at the symbols and the integers, and joins the relevant parts.
splitAt[list_,pat_]:=Module[{pos=Flatten@Position[list,pat]},MapThread[list[[#1;;#2]]&,{pos,Append[Rest@pos-1,-1]}]];
Flatten[Function[u,Prepend[#,First@u]&/@splitAt[u,_Integer]]/@splitAt[testList,_?(#==Abc||#==Def&)],1]
(*{{Abc, 2, f, g}, {Abc, 1, h}, {Def, 2, j, k}, {Def, 1, w, z, y}}*)
list = {Abc, d, e, 2, f, g, 1, h, Def, q, 2, j, k, 1, w, z, y};
Here's something more readable:
Join @@ SequenceCases[
list
, { head : Abc | Def
, Except[_Integer] ...
, rest : Except[Abc | Def] ..
} :> Flatten /@ Thread[{head, Split[{rest}, Not@IntegerQ[#2] &]}]
]
Consider the following approach, which is going to sequentially build the output, by treating the entries in the input list in an appropriate way, depending on their position relative to flag Def
.
(Please note that if you are interested in the code of this answer you can skip all the commenting and go straight to the end of this answer.)
First, we need to obtain the positions of the flag Def
and the integers in the input list, respectively.
{{fpos}} = Position[list, Def]
ipos = Position[list, _Integer]
Evaluating the code above with our testList
as input returns
{{9}} {{4}, {7}, {11}, {14}}
(please note that fpos
evaluates to 9
, not {{9}}
).
Next, we are going to create two sub-lists of indexes, namely a sub-list with the indexes of integer entries in the input list that are located before the flag (in this list, we will include the position of the Def
flag, too) and a sub-list of indexes of integer entries in the input list that are located after the flag.
pos1 = Cases[ipos, {n_} :> n /; n < fpos]
If[fpos - 1 > Last[pos1], pos1 = Flatten[{pos1, fpos}]]
pos2 = Cases[ipos, {n_} :> n /; n > fpos]
Evaluating this, yields
{4, 7} {4, 7, 9} {11, 14}
(The second line in the code segment above, is used to identify the case where there are intervening symbols between the last integer before the Def
flag and he flag itself-as is the case with the testList
input list).
The following step, entails assembling the core of the output list out of the index lists pos1
and pos2
.
res11 = Apply[Take[list, Flatten[{##} + {0, -1}]] &, Partition[pos1, 2, 1], 1]
res21 = Apply[Take[list, Flatten[{##} + {0, -1}]] &, Partition[pos2, 2, 1], 1]
res22 = Drop[list, Last[pos2 - 1]]
Both res11
and res21
compose the core of the output sub-lists as does res22
, which is introduced to tackle the last admissible integer entry in the input list; Partition[<positions>,2,1]
supplies the input for Take
ing the appropriate parts of the input list.
Evaluating the code, produces
{{2, f, g}, {1, h}} {{2, j, k}} {1, w, z, y}
Essentially, the transformation of input to output is complete; the only thing left is to format the output sub-lists appropriately, namely to insert the Abc
and Def
markers as intended.
res1 = Apply[{Abc, ##} &, Flatten[{res11}, 1], 1]
res2 = Apply[{Def, ##} &, Flatten[{res21, {res22}}, 1], 1]
Flatten[{res1, res2}, 1]
Evaluation of this last segment of code returns
{{Abc, 2, f, g}, {Abc, 1, h}} {{Def, 2, j, k}, {Def, 1, w, z, y}}
and, what is effectively the requested output
{{Abc, 2, f, g}, {Abc, 1, h}, {Def, 2, j, k}, {Def, 1, w, z, y}}
Having said that-on a personal note-I have come to realize that when I need to resort to similar sequential treatment of output, it is highly likely that I could have reorganized my code in a different way, to begin with. It might not be the case here (obviously, I don't know) but I'm pretty sure that, at some level and to a certain extent, Mathematica / Wolfram Language can produce structured output with a lot less trouble.
All the code bundled in a Module
:
f[list_] := Module[{fpos, ipos, pos1, pos2, res11, res21, res22, res1, res2},
{{fpos}} = Position[list, Def];
ipos = Position[list, _Integer];
pos1 = Cases[ipos, {n_} :> n /; n < fpos];
If[fpos - 1 > Last[pos1], pos1 = Flatten[{pos1, fpos}]];
pos2 = Cases[ipos, {n_} :> n /; n > fpos];
res11 = Apply[Take[list, Flatten[{##} + {0, -1}]] &, Partition[pos1, 2, 1],1];
res21 = Apply[Take[list, Flatten[{##} + {0, -1}]] &, Partition[pos2, 2, 1],1];
res22 = Drop[list, Last[pos2 - 1]];
res1 = Apply[{Abc, ##} &, Flatten[{res11}, 1], 1];
res2 = Apply[{Def, ##} &, Flatten[{res21, {res22}}, 1], 1];
Flatten[{res1, res2}, 1]
]
and evaluating
f[testList] == desiredList
should return True
.