7
$\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$
4
  • 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$
    – vapor
    Commented Mar 28, 2017 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
    Commented Mar 28, 2017 at 8:41
  • $\begingroup$ @happyfish, yes your code is a possible solution $\endgroup$
    – Sascha
    Commented Mar 28, 2017 at 8:44
  • $\begingroup$ OK, I undeleted my answer. But I think it is not very contributive after all... $\endgroup$
    – vapor
    Commented Mar 28, 2017 at 8:45

6 Answers 6

5
$\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$
2
$\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
  • 1
    $\begingroup$ Also DeleteCases[ ArrayReshape[a, {p, \[LeftCeiling]Length@a/p\[RightCeiling]}, x], x, 2] ... +1 $\endgroup$
    – Michael E2
    Commented Nov 4, 2019 at 3:01
2
$\begingroup$

This seems to do the job:

spl[lst_, n_] := TakeList[lst, Subdivide[1, Length@lst + 1, n] // Round // Differences];
$\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$

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.