10
$\begingroup$

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?

$\endgroup$
  • $\begingroup$ The first part of your question I answered here: (7511) $\endgroup$ – Mr.Wizard Dec 3 '15 at 23:16
12
$\begingroup$

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

Fold[Rule, #] & /@ 
 Internal`PartitionRagged[{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 /@ 
 Internal`PartitionRagged[{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@#]} & /@ 
  Internal`PartitionRagged[{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10}, {1, 2, 3, 4}]

(* {{a1}, {a2 -> a3}, {a4 -> a5 -> a6}, {a7 -> a8 -> a9 -> a10}} *)
$\endgroup$
  • 2
    $\begingroup$ With // Partition[#, 1] & in the end to yield the exact requested output. $\endgroup$ – anderstood Dec 3 '15 at 18:05
  • $\begingroup$ @anderstood. Or {#} &/@ result. $\endgroup$ – march Dec 3 '15 at 18:29
  • 1
    $\begingroup$ @march List /@ result is probably clearer for a beginner $\endgroup$ – Dr. belisarius Dec 3 '15 at 18:56
  • 2
    $\begingroup$ @belisariushassettled. And for an established user/expert, too. $\endgroup$ – march Dec 3 '15 at 18:57
6
$\begingroup$

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}}
$\endgroup$
5
$\begingroup$

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

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}}*)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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