5
$\begingroup$

I have the following list consisting of strings and integers:

testList = {a, Abc, b, 3, a, c, d, b, Abc, m, n, 2, q, r, w, Def, q, 3, a, Def, 1, a, g};

and I would like to obtain the following sublists:

desiredResult = {{Abc, 3, a, c, d, b}, {Abc, 2, q, r, w}, {Def, 3, a},  ={Def, 1, a, g}} 

This involves eliminating everything before the first instance of the "Abc" string, picking the "Abc" string, omitting anything between it and the next integer, and adding everything following this integer until the next instance of "Abc" etc. I think it's easier to look at the example than to describe it in words. I'm not sure how to use pattern matching for this, any ideas would be gratefully received.

$\endgroup$
  • 1
    $\begingroup$ This should be a job for SequenceCases, but I don't have Mathematica 10.1 (only 10.0), so I can't test it. $\endgroup$ – march Nov 30 '17 at 22:35
7
$\begingroup$
SequenceCases[
  testList
, { head : Abc | Def, Except[_Integer] ..., rest : Except[Abc | Def] ..
  } :> {head, rest}
]
$\endgroup$
  • 1
    $\begingroup$ This is great ... +1 $\endgroup$ – mrz Dec 1 '17 at 20:27
  • $\begingroup$ @mrz thanks :-) $\endgroup$ – Kuba Dec 1 '17 at 20:32
3
$\begingroup$
testList = {"a", "Abc", "b", 3, "a", "c", "d", "b",
   "Abc", "m", "n", 2, "q", "r", "w", "Def", "q",
   3, "a", "Def", 1, "a", "g"};

Apply[Join, {#1, #2 //. {a_String, b___} :> {b}} & @@@ 
  Rest@Partition[SplitBy[Join[{"Abc", 1},
      testList], # == "Abc" || # == "Def" &], 2], {1}]
{{Abc, 3, a, c, d, b}, {Abc, 2, q, r, w}, {Def, 3, a}, {Def, 1, a, g}}

With trickier data.

testList = {"a", "Abc", "b", 3, "a", "c", "d", "b",
   "Abc", "Def", "Def", 1, "a", "g"};

Apply[Join, {#1, #2 //. {a_String, b___} :> {b}} & @@@   
   Rest@Partition[
     SplitBy[Join[{"Abc", 1}, testList /.
        {"Abc" -> Sequence[Null, "Abc"],
         "Def" -> Sequence[Null, "Def"]}],
      # === "Abc" || # === "Def" &], 2], {1}] /. Null -> Nothing
{{Abc, 3, a, c, d, b}, {Abc}, {Def}, {Def, 1, a, g}}
$\endgroup$
  • $\begingroup$ Thanks will work on this more. $\endgroup$ – Suite401 Nov 30 '17 at 23:42
2
$\begingroup$

In this code q = 1 means "waiting for integer" and if not then q = 0.

ToExpression[Reap[Module[{n = 0, q = 0}, Sow[0, 0];
    Do[If[UpperCaseQ[#], Sow[i, ++n]; q = 1,
     If[q =!= 1, Sow[i, n], If[DigitQ[#], q = 0; Sow[i, n]]]] &[
      StringTake[i, 1]], {i, ToString /@ testList}]]][[2, 2 ;;]]]
{{Abc, 3, a, c, d, b}, {Abc, 2, q, r, w}, {Def, 3, a}, {Def, 1, a, g}}
$\endgroup$
  • $\begingroup$ +1 Works with my trickier data too. $\endgroup$ – Chris Degnen Nov 30 '17 at 23:33
1
$\begingroup$

My take on this is to deconstruct the list using replacement rules, generating a sort-of linked list along the way. It can likely be made more elegant, but here's what I came up with.

deconstruct[expr_List, vars_List] := Module[
  {lst, alts = Alternatives @@ vars},
  lst = testList /. {Shortest[___], a : alts, x___} :> {a, x};
  lst = lst //. {a : alts, Shortest[b___], c : alts, d___} :> {l[a, b], {c, d}} // Flatten;
  lst /. {Longest[a__l], b__} :> {a, l[b]} /. l -> List
 ]

Usages:

testList = {a, Abc, b, 3, a, c, d, b, Abc, m, n, 2, q, r, w, Def, q, 3, a, Def, 1, a, g};
deconstruct[testList, {Abc, Def}]
(* {{Abc, b, 3, a, c, d, b}, {Abc, m, n, 2, q, r, w}, {Def, q, 3, a}, {Def, 1, a, g}} *)
deconstruct[testList, {Abc}]
(* {{Abc, b, 3, a, c, d, b}, {Abc, m, n, 2, q, r, w, Def, q, 3, a, Def, 1, a, g}} *)

With Chris Degnen's trickier data:

testList = {"a", "Abc", "b", 3, "a", "c", "d", "b", "Abc", "Def", "Def", 1, "a", "g"};
deconstruct[testList, {Abc}]
(* {{"Abc", "b", 3, "a", "c", "d", "b"}, {"Abc"}, {"Def"}, {"Def", 1, "a", "g"}} *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.