-1
$\begingroup$

Given a list list, how can it be transformed so that every three elements selected from left to right form a sublist, with the first two elements of each sublist enclosed in curly braces? The result should be list1.

list = {a, b, c, a, b, d, a, b, e, a, b, f, a, b, g}
list1 = {{{a, b}, c}, {{a, b}, d}, {{a, b}, e}, {{a, b}, f}, {{a, b}, 
   g}}

I attempted this approach, but it didn't yield the correct results.

list = {a, b, c, a, b, d, a, b, e, a, b, f, a, b, g};
list1 = Apply[{{#1, #2}, #3} &, Partition[list, 3]]
$\endgroup$
2

6 Answers 6

7
$\begingroup$
SequenceSplit[list, {a_, b_, c_} :> {{a, b}, c}]

(* {{{a, b}, c}, {{a, b}, d}, {{a, b}, e}, {{a, b}, f}, {{a, b}, g}} *)

Or

MapApply[{{#1, #2}, #3} &]@Partition[list, 3]

 (* {{{a, b}, c}, {{a, b}, d}, {{a, b}, e}, {{a, b}, f}, {{a, b}, g}} *)

Or

MapThread[{{#1, #2}, #3} &, Transpose@Partition[list, 3]]


(* {{{a, b}, c}, {{a, b}, d}, {{a, b}, e}, {{a, b}, f}, {{a, b}, g}} *)

In addition, using the undocumented (but very useful) fifth argument of Padding

Partition[list, 3, 3, 1, {}, {{#1, #2}, #3} &]

(* {{{a, b}, c}, {{a, b}, d}, {{a, b}, e}, {{a, b}, f}, {{a, b}, g}} *) 

Just for fun

Using Map to replace the Head List in the partitioned list with a 'pure' function:

Map[Replace[List :> ({{#1, #2}, #3} &)], 
    Partition[list, 3], {2}, Heads -> True]

(* {{a, b, c}, {a, b, d}, {a, b, e}, {a, b, f}, {a, b, g}} *)

A similar approach using MapAt

MapAt[Replace[List :> ({{#1, #2}, #3} &)], 
    Partition[list, 3], {All, 0}]
$\endgroup$
8
$\begingroup$
list = {a, b, c, a, b, d, a, b, e, a, b, f, a, b, g};

SequenceCases[list, {a_, b_, c_} :> {{a, b}, c}]
SequenceReplace[list, {a_, b_, c_} :> {{a, b}, c}]

{{{a, b}, c}, {{a, b}, d}, {{a, b}, e}, {{a, b}, f}, {{a, b}, g}}


The reader can add another element to the list to see the difference in outputs between the two commands.

$\endgroup$
8
$\begingroup$

This works:

Partition[list, 3] /. {x_, y_, z_} -> {{x, y}, z}

(*{{{a,b},c},{{a,b},d},{{a,b},e},{{a,b},f},{{a,b},g}}*)

Edit:

The comment from @march makes a very important point. The case in which the length of list partitions into 3 means that ReplaceAll ( /. ) applies the rule successfully to the top level, which is not what we want. The use of Replace with a specified levelspec eliminates this ambiguity.

list = {a, b, c, a, b, d, a, b, e};

Partition[list, 3] /. {x_, y_, z_} -> {{x, y}, z}

(*{{{a,b,c},{a,b,d}},{a,b,e}}*)

(*This is not what was intended*)

Replace[Partition[list, 3], {x_, y_, z_} -> {{x, y}, z}, 1]

(*{{{a,b},c},{{a,b},d},{{a,b},e}}*)

(*This is what was intended*)

Partition[list, 3]

(*{{a,b,c},{a,b,d},{a,b,e}}*)

(*In the 2nd list the partitioned list matched the rule at the top \
{0} level so we don't get what we want *)
$\endgroup$
3
  • 3
    $\begingroup$ In this situation, it's safer to use Replace as opposed to ReplaceAll so that you can replace at the right level. That is, do Replace[Partition[list, 3], {x_, y_, z_} -> {{x, y}, z}, 1] instead. (Consider what happens for list = {a, b, c, a, b, d, a, b, e}.) $\endgroup$
    – march
    Commented Oct 4 at 3:43
  • 1
    $\begingroup$ Thank you, @march. I think you make an important point. I have edited my answer inspired by your comment. $\endgroup$ Commented Oct 4 at 16:59
  • 2
    $\begingroup$ For a very succinct elucidation of this point, see this post by @Szabolcs $\endgroup$
    – user1066
    Commented Oct 4 at 18:04
7
$\begingroup$
ArrayReshape[list, {Length@list/3, 3}] /. {a_, b_, c_} :> {{a, b}, c}
$\endgroup$
7
$\begingroup$
{Most[#], Last[#]} & /@ Partition[list, 3]
$\endgroup$
7
$\begingroup$
BlockMap[{{#[[1]], #[[2]]}, #[[3]]} &, list, 3]

Update inspired by @ydd:

BlockMap[Comap[{Most, Last}], list, 3]

Comap is new in version 14 and is marked as experimental

$\endgroup$

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