3
$\begingroup$

Is there an inbuilt function/option or an elegant composition of functions for partitioning a list into exactly $n$ sublists? I played around with Partition and its options but could not find anything suitable by itself. The general requirement is that the Length of the resulting splitted list is $n$. A few interesting corner cases:

  • Splitting a list into $n=1$ parts should result in the list wrapped with List, e.g. splitting {1,2,3} into $n=1$ parts yields {{1,2,3}} because Length[{{1,2,3}}] is 1
  • Splitting lists with fewer than $n$ elements by padding with empty lists e.g. {1,2}into $n=3$ parts should yield {{1}, {2}, {}}
  • Splitting lists of uneven length into an even number of parts or vice versa e.g. {1,2,3} into $n=2$ parts should either yield {{1,2},{3}} or {{1}, {2,3}}. It doesn't matter to me which, but behavior should be consistent.

Right now I use

ClearAll@splitList;
splitList[1][list_] := {list};
splitList[parts_Integer][list_] := 
  With[{n = Ceiling[Length[list]/parts]}, 
     Partition[list, UpTo[n]] // 
     Replace[
      {prev : Repeated[{___}, parts - 1]} :> 
      {prev, Apply[Sequence]@Table[{}, {parts - Length@{prev}}] } 
     ]
  ]

I feel that the concept of splitting a list into exactly $n$ sublists is something that is easy to express but involves difficult corner cases and thus could be something that is already implemented in Mathematica, much like with regular Partition and UpTo.

$\endgroup$
  • 1
    $\begingroup$ In your program, splitList[4][Range[9]] returns {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {}} is it intended? How about {{1,2,3,4,5,6,7,8,9},{},{},{}}? $\endgroup$ – happy fish Mar 28 '17 at 8:34
  • $\begingroup$ @happy fish thanks for this observation; this is yet another corner case that is ill defined and does depend on what one wishes to accomplish. My application uses only needs the special case $n=2$ and I want chunks of equal size where possible but any solution is interesting in the scope of this broader question. In a build-in function I would expect the partitioning scheme as an option. $\endgroup$ – Sascha Mar 28 '17 at 8:41
  • $\begingroup$ @happyfish, yes your code is a possible solution $\endgroup$ – Sascha Mar 28 '17 at 8:44
  • $\begingroup$ OK, I undeleted my answer. But I think it is not very contributive after all... $\endgroup$ – happy fish Mar 28 '17 at 8:45
4
$\begingroup$

This seems to return same results as your code:

ms = PadRight[Partition[#, UpTo[Ceiling[Length[#]/#2]]], #2, {{}}]&;

Check:

r1 = splitList[#][{1, 2, 3, 4, 5, 6}] & /@ Range[1, 10];
r2 = ms[{1, 2, 3, 4, 5, 6}, #] & /@ Range[1, 10];
r1 == r2

True

$\endgroup$
1
$\begingroup$

I am not sure I have understood this. Perhaps this will motivate clarification or the correct or better answer:

f[a_, n_] := 
 Module[{a2}, (If[n > Length[a], a2 = PadRight[a, n], a2 = a]; 
   Internal`PartitionRagged[a2, #] & /@ 
     Permutations[IntegerPartitions[Length[a2], {n}][[1]]] /. {0 -> 
      Sequence[]})]

e.g. Grid[{Range@#, f[Range@#, 4]} & /@ Range[3, 10]]:

enter image description here

$\endgroup$
1
$\begingroup$

A simpler version.

p[list_, n_] := 
 Module[{res = Partition[list, UpTo[Ceiling[Length[list]/n]]]}, 
  Join[res, Table[{}, n - Length[res]]]]
$\endgroup$
1
$\begingroup$

Assuming {} should only be used when n is greater than Length[list]:

split[L_, n_] := PadRight[MapThread[L[[Span[##]]] &, {Most[# + 1], Rest[#]} &[
                      Round[Range[0, #, Max[#/n, 1]] &[Length[L]]]]], {n}, {{}}]

list = Range[17];
p[list, 1]
p[list, 12]
p[list, 25]

{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}}

{{1}, {2, 3}, {4}, {5, 6}, {7}, {8}, {9, 10}, {11}, {12, 13}, {14}, {15, 16}, {17}}

{{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, \ {14}, {15}, {16}, {17}, {}, {}, {}, {}, {}, {}, {}, {}}

$\endgroup$
1
$\begingroup$

This seems to do the job:

spl[lst_, n_] := TakeList[lst, Subdivide[1, Length@lst, n] // Round // Differences];
$\endgroup$
1
$\begingroup$

ArrayReshape seems quite clean to my eye, even with replacement for null padding. It also works in v9.0 and after, whereas UpTo was introduced in v10.3.

Module[{x},
  splitIn[p_Integer?Positive][a_?ListQ] :=
    ArrayReshape[a, {p, ⌈Length@a/p⌉}, x] /. x -> Sequence[]
]
$\endgroup$
  • 1
    $\begingroup$ Also DeleteCases[ ArrayReshape[a, {p, \[LeftCeiling]Length@a/p\[RightCeiling]}, x], x, 2] ... +1 $\endgroup$ – Michael E2 Nov 4 at 3:01

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.