5
$\begingroup$

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.

$\endgroup$
4
  • $\begingroup$ 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? $\endgroup$ Commented Jun 7, 2020 at 0:01
  • $\begingroup$ @CATrevillian Yes, that's what I meant. I'll edit to reflect that, thank you! $\endgroup$
    – Nico A
    Commented Jun 7, 2020 at 0:01
  • $\begingroup$ 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? $\endgroup$ Commented Jun 7, 2020 at 0:04
  • $\begingroup$ @CATrevillian Good point. Calls at the end with no response should also just be ignored, left out of the final list. $\endgroup$
    – Nico A
    Commented Jun 7, 2020 at 0:06

2 Answers 2

7
$\begingroup$
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.

$\endgroup$
7
$\begingroup$
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"}}
$\endgroup$
4
  • 1
    $\begingroup$ Neat function, I'd never heard of it before! This should work. $\endgroup$
    – Nico A
    Commented Jun 7, 2020 at 0:12
  • 1
    $\begingroup$ @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? $\endgroup$ Commented Jun 7, 2020 at 0:35
  • 1
    $\begingroup$ @CATrevillian, thank you; good point. I updated with a method that works for your example too. $\endgroup$
    – kglr
    Commented Jun 7, 2020 at 2:21
  • 1
    $\begingroup$ @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. $\endgroup$ Commented Jun 9, 2020 at 9:53

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.