6
$\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$
2
  • 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
    Commented Nov 30, 2017 at 22:35
  • $\begingroup$ The OP mentions that the data has Strings such as "Abc" etc. The edits and the answers over the years have made this page difficult to follow due to two parallel strains of input. The title is not particularly informative either. $\endgroup$
    – Syed
    Commented Jan 20 at 7:07

5 Answers 5

9
$\begingroup$
SequenceCases[
  testList
, { head : Abc | Def, Except[_Integer] ..., rest : Except[Abc | Def] ..
  } :> {head, rest}
]
$\endgroup$
2
  • 1
    $\begingroup$ This is great ... +1 $\endgroup$
    – mrz
    Commented Dec 1, 2017 at 20:27
  • $\begingroup$ @mrz thanks :-) $\endgroup$
    – Kuba
    Commented Dec 1, 2017 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$
1
  • $\begingroup$ Thanks will work on this more. $\endgroup$
    – Suite401
    Commented Nov 30, 2017 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$
1
  • $\begingroup$ +1 Works with my trickier data too. $\endgroup$ Commented Nov 30, 2017 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$
1
$\begingroup$
list = 
 {a, Abc, b, 3, a, c, d, b, Abc, m, n, 2, q, r, w, Def, q, 3, a, Def, 1, a, g};

We need to prepend the dummy x in case list starts with Abc or Def

Rest @ Split[Prepend[x] @ list, StringLength[ToString @ #2] == 1 &] /.
 {a_, ___, b_Integer, c__} :> {a, b, c}

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

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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