# Grouping list elements according to a condition

I have a list of "calls and responses", which are pairs of a message and then either a $$0$$, for a call, or a $$1$$, for a response. There is some amount of calls, and some amount, potentially unequal, of responses in answer. An example of such a list might be the following:

{{"Call",0},
{"Call 2",0},
{"Response",1},
{"Call 3",0},
{"Response",1},
{"Response 2",1}}


The text is arbitrary, and won't actually contain "call" or "response". What I am trying to do is group this list into an association of calls to responses, so that the list above would become

{{"Call","Call 2"}->{"Response"},
{"Call  3"}->{"Response","Response 2"}}


I looked at GroupBy and GatherBy but they seem to re-order the list, which I don't want to do. The order should stay the same, just with calls and responses sorted together. How can I achieve this elegantly?

Bonus points if the answer can also cut out extraneous responses with no calls at the beginning of the list, and extraneous calls with no response at the end of the list.

• You give 6 separate lists. Can we assume that these are actually 6 parts of an ordered list of calls and responses for which the calls are associated with the responses that follow them until calls are encountered again? – CA Trevillian Jun 7 at 0:01
• @CATrevillian Yes, that's what I meant. I'll edit to reflect that, thank you! – Nico A Jun 7 at 0:01
• Now, your bonus points ask would only apply to the beginning of the list? As in, there’s some responses before the initial calls and they shouldn’t be included in any possible associations. What should happen if there are calls at the end of the list with no responses? – CA Trevillian Jun 7 at 0:04
• @CATrevillian Good point. Calls at the end with no response should also just be ignored, left out of the final list. – Nico A Jun 7 at 0:06

crl={{"Call",0},{"Call 2",0},{"Response",1},{"Call 3",0},{"Response",1},{"Response 2",1}};

SequenceCases[crl,{c:{_,0}..,r:{_,1}..}:>First/@{c}->First/@{r}]

{{Call,Call 2}->{Response}, {Call 3}->{Response,Response 2}}

crl1={{"Bad Response",1},{"Call",0},{"Call 2",0},{"Response",1},{"Call 3",0},{"Response",1},{"Response 2",1},{"Bad Call",0}};

SequenceCases[crl1,{c:{_,0}..,r:{_,1}..}:>First/@{c}->First/@{r}]


Same as above.

Select[Length @ # > 1 &] @ SequenceSplit[data, p : {{_, 0} ..., {_, 1} ..} :> p]

 {{{"Call", 0}, {"Call 2", 0}, {"Response", 1}},
{{"Call 3", 0}, {"Response", 1}, {"Response 2", 1}}}

SequenceSplit[data, {p1 : {_, 0} ..., p2 : {_, 1} ..} :> {p1} -> {p2}]

{{{"Call", 0}, {"Call 2", 0}} -> {{"Response", 1}},
{{"Call 3", 0}} -> {{"Response", 1}, {"Response 2", 1}}}

SequenceSplit[data, {p1 : {_, 0} ..., p2 : {_, 1} ..} :> {p1}[[All, 1]] -> {p2}[[All, 1]]]

{{"Call", "Call 2"} -> {"Response"},
{"Call 3"} -> {"Response", "Response 2"}}


Update: An alternative approach using Split and SplitBy:

Rule @@@ Map[SplitBy[#, Last][[All, All, 1]] &]@
Select[Length @ # > 1 &] @ Split[data, {#[[2]], #2[[2]]} != {1, 0} &]

  {{"Call", "Call 2"} -> {"Response"},
{"Call 3"} -> {"Response", "Response 2"}}

• Neat function, I'd never heard of it before! This should work. – Nico A Jun 7 at 0:12
• @kglr this method does not accomplish the bonus points ask. Use my crl1 as an example, and you get this: {{{“Bad Response”, 1},...,{“Bad Call”, 0}}}. Perhaps an additional check for Rule patterns after that would be a workaround? – CA Trevillian Jun 7 at 0:35
• @CATrevillian, thank you; good point. I updated with a method that works for your example too. – kglr Jun 7 at 2:21
• @kglr you can still use your SequenceSplit method by only using .. as opposed to using ... with the first pattern, then post-processing the List with /.List[List[__]]->Nothing, or, if you use ... for both, /.Rule[List[],__]|Rule[__,List[]]->Nothing. – CA Trevillian Jun 9 at 9:53