Split according to list and apply rule

I have a list which looks some thing like this

{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10}


Now I want to split it according to lengths {1,2,3,4} so that I would have

{{a1},{ a2, a3}, {a4, a5, a6},{ a7, a8, a9, a10}}


and I want to apply the rule with itself. For example: after rule application, the answer should become

{{a1},{a2->a3},{a4->a5->a6},{a7->a8->a9->a10}}.


I have tried to use Split and Partition function but not getting the desired result. Is there any other function that accomplishes this? If not how can I do this?

• The first part of your question I answered here: (7511) Dec 3, 2015 at 23:16

Depending on the precedence you want for Rule, you can use either

Fold[Rule, #] & /@
InternalPartitionRagged[{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10}, {1, 2, 3, 4}]

(* {a1, a2 -> a3, (a4 -> a5) -> a6, ((a7 -> a8) -> a9) -> a10} *)


or

Fold[Rule[#2, #1] &, #] & /@ Reverse /@
InternalPartitionRagged[{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10}, {1, 2, 3, 4}]

(* {a1, a2 -> a3, a4 -> a5 -> a6, a7 -> a8 -> a9 -> a10} *)


To end up with the requested output, you can evalute List /@ on the resulting expressions.

Update

To avoid mapping twice in the last solution, one could use Fold[Rule[#2, #1] &, Reverse@#] & instead of Fold[Rule[#2, #1] &, #] & /@ Reverse.

Also, instead of using List /@ on the output as suggested above, one could directly enclose the Fold function between braces, e.g.

{Fold[Rule[#2, #1] &, Reverse@#]} & /@
InternalPartitionRagged[{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10}, {1, 2, 3, 4}]

(* {{a1}, {a2 -> a3}, {a4 -> a5 -> a6}, {a7 -> a8 -> a9 -> a10}} *)

• With // Partition[#, 1] & in the end to yield the exact requested output. Dec 3, 2015 at 18:05
• @anderstood. Or {#} &/@ result. Dec 3, 2015 at 18:29
• @march List /@ result is probably clearer for a beginner Dec 3, 2015 at 18:56
• @belisariushassettled. And for an established user/expert, too. Dec 3, 2015 at 18:57

The partitioning aspect of your question is already addressed in detail in Partitioning with varying partition size.

The second aspect of your question amounts to performing a right fold. For the case at hand this might be done most easily using pattern matching:

rule[a___, x_, y_] := rule[a, x -> y]
rule[x_] := x


This is essentially a multi-argument Rule with a defined precedence. It does this:

rule[a1, a2, a3, a4]

a1 -> a2 -> a3 -> a4


You can then Apply this function to a list, or list of lists. Using my partitionBy function from the referenced Q&A your operation is then:

in = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10};

List /@ rule @@@ partitionBy[in, # &]

{{a1}, {a2 -> a3}, {a4 -> a5 -> a6}, {a7 -> a8 -> a9 -> a10}}


Xavier also provided a right fold method, and one that can be much more efficient in certain cases due to the optimizations within Fold. Let's make it an abstraction, and add an Operator Form while we're at:

Default[foldr] = Sequence[];
foldr[f_][p__] := foldr[f, p]
foldr[f_, x_., a : _[__]] := Fold[#2 ~f~ #1 &, x, Reverse @ a]


It does this:

foldr[oo][{a1, a2, a3, a4}]

oo[a1, oo[a2, oo[a3, a4]]]


Your operation is then:

in = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10};

List /@ foldr[Rule] /@ partitionBy[in, # &]

{{a1}, {a2 -> a3}, {a4 -> a5 -> a6}, {a7 -> a8 -> a9 -> a10}}


If you don't want to use the handy but undocumented PartitionRagged, you could go by:

l  = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10};
r = Range[1/2 (-1 + Sqrt[1 + 8 Length@l])]
ls = Transpose@{# - PadLeft[Most@r, Length@r], #} &@Accumulate@r;
l[[#1 ;; #2]] & @@@ ls

(*{{a1}, {a2, a3}, {a4, a5, a6}, {a7, a8, a9, a10}}*)

• "List /@ result is probably clearer for a beginner" – belisarius has settled 16 mins ago :)
– Kuba
Dec 3, 2015 at 19:14
• @Kuba What am I missing?:( Dec 3, 2015 at 19:19
• A solution clear for beginner :)
– Kuba
Dec 3, 2015 at 19:20
• @Kuba Ahhh ... that's Xavier's ! :D Dec 3, 2015 at 19:21
• A documented alternative to PartitionRagged could be: FoldPairList[TakeDrop, {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10}, {1, 2, 3, 4}].
– user31159
Dec 3, 2015 at 19:44

Just for something different:

pf[lst_, pt_] := Module[{p = Join @@ (Table[#, {#}] & /@ pt)},
Last@Reap[Inner[Sow, lst, p, List], _, #2 &]]


So,

m = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10};
pf[m, {1, 2, 3, 4}]


yields:

(*{{a1}, {a2, a3}, {a4, a5, a6}, {a7, a8, a9, a10}}*)

lst = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10};
ord = {1, 2, 3, 4}

Prepend[
Map[Take[lst, #] &,
Map[{#[[1]] + 1, #[[2]]} &, Partition[Accumulate[ord], 2, 1]]
],
{lst[[1]]}
] //. {a___, {b_, c_, p___}, d___} :> {a, {b -> c, p}, d}

{{a1}, {a2 -> a3}, {(a4 -> a5) -> a6}, {((a7 -> a8) -> a9) -> a10}}

list = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10};

{ToExpression @ StringRiffle[#, "->"]} & /@ TakeList[list, {1, 2, 3, 4}]


{{a1}, {a2 -> a3}, {a4 -> a5 -> a6}, {a7 -> a8 -> a9 -> a10}}

TakeList since V 11.2 and StringRiffle since V 10.1

Clear["Global*"];
list = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10};
t = TakeList[list, {1, 2, 3, 4}];
TextString[#, ListFormat -> {"{ ", "->", " }"}] & /@ t // ToExpression


{{a1}, {a2 -> a3}, {a4 -> a5 -> a6}, {a7 -> a8 -> a9 -> a10}}

Update

(1)

List@*ResourceFunction["FoldRight"][Rule] /@
TakeList[{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10}, {1, 2, 3, 4}]

(* {{a1}, {a2 -> a3}, {a4 -> a5 -> a6}, {a7 -> a8 -> a9 -> a10}} *)


(2)

List@Fold[ReverseApplied[Rule], #1, ##2] & /@
Reverse[TakeList[{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10}, {1, 2, 3, 4}], 2]

(* {{a1}, {a2 -> a3}, {a4 -> a5 -> a6}, {a7 -> a8 -> a9 -> a10}} *)


Right Fold

ResourceFunction["FoldRight"][f]@{a, b, c, d}

(* f[a, f[b, f[c, d]]] *)

Fold[ReverseApplied[f], #1, ##2] &@Reverse[{a, b, c, d}]

(* f[a, f[b, f[c, d]]] *)