# Concise way to generate multiset lists

I wrote the following to generate a multiset with the same number of items over a fixed range:

ConstantArray[#, 3]& /@ Range[9] // Flatten

{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9}


This approach uses four named functions (Range, Flatten, ConstantArray and Map (/@)) and one pure function (ending with &).
Is there a way to do the same thing with fewer functions?

Also, if I wanted to have variable numbers of items in my multiset how would I do that?
For example, let's say I wanted a multiset like { 1, 1, 1, 1, 2, 2, 3, 4, 4, 4, 6, 6, 7, 8, 8, 8 }.

By a "variable number of items" I do not mean random, I mean a specified variable number.

   f[n_, k_] := Table[ConstantArray[i, n], {i, k}]


Now you can vary the number of items of each:

f[3, 9] // Flatten


{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9}

f[4, 10] // Flatten


{1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10}

for random Situation:

fRandom[n_, k_] := Table[ConstantArray[i, RandomInteger[{1, n}]], {i, k}]


For your second situation try this;

Inner[ConstantArray, Range[8], {4, 2, 1, 3, 0, 2, 1, 3}, List] // Flatten


{1, 1, 1, 1, 2, 2, 3, 4, 4, 4, 6, 6, 7, 8, 8, 8}

• for constant k, one could also do: Flatten[Table[i, {i, 10}, {j, 3}]] Oct 17 '13 at 17:05
• @PinguinDirk. Fo shizzle :) Oct 17 '13 at 17:09
• I do not want random numbers of items. I want specific numbers of each item in the multiset. Oct 17 '13 at 18:11
• @TylerDurden, Well, initially you did not specify this. How exactly do you want to specify those numbers, you need to be more specific so we know what you want. Oct 17 '13 at 18:33
• @TylerDurden. See my update. Oct 17 '13 at 19:32

These should be the most efficient and tersest

Ceiling[ Range @ 27 / 3 ]


or

Array[ Ceiling[#/3] &, 27]


yield

{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9}


they both can be tersely written in the Front End (see details of Ceiling) as:

or

For the second problem there are many approaches, here is a solution with a determined number of items ( Count[{1, 1, 1, 1, 2, 2, 3, 4, 4, 4, 6, 6, 7, 8, 8, 8}, #] & /@ Range[8]) as in your example:

Inner[ ConstantArray, Range @ 8, {4, 2, 1, 3, 0, 2, 1, 3}, Join]

{1, 1, 1, 1, 2, 2, 3, 4, 4, 4, 6, 6, 7, 8, 8, 8}


or

Flatten @ MapThread[ ConstantArray, {Range @ 8, {4, 2, 1, 3, 0, 2, 1, 3}}]

• I do not want random numbers of items. I want specific numbers of each item in the multiset. Oct 17 '13 at 18:12
• @TylerDurden So you've got it. Oct 17 '13 at 18:24

To me, the most straightforward way seems to be:

Flatten[ConstantArray[Range[9], 3], {2, 1}]
(* {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9} *)


i.e., instead of repeating each element in the list 3 times, you repeat the entire list 3 times and flatten it using the appropriate second argument.

• +1 -- this is the same method I use, though usually with infix: Range@9 ~ConstantArray~ 3 ~Flatten~ {2, 1} :-) Dec 29 '13 at 20:08
• @Mr.Wizard Pfft lame... 1 ~Range~ 9 ~ConstantArray~ 3 ~Flatten~ (2 ~List~ 1) :P
– rm -rf
Dec 29 '13 at 20:59
• reductio ad absurdum Dec 29 '13 at 22:33

No Rules?, so this is one from Szabolc's site:

 Range[9] /. n_Integer :> Sequence[n, n, n]

{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9}


Since you haven't described how those numbers should vary for second part, maybe something like:

Sort@RandomInteger[{1, 10}, 27]

{1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 10}


Ok, let's say you have list of numbers which are referring to consecutive integers repetition:

Flatten@MapIndexed[ConstantArray[#2, #] &, {4, 3, 5, 1, 2, 3}]

{1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 5, 5, 6, 6, 6}


Or similar:

i = 0;
Flatten[ConstantArray[++i, #] & /@ {4, 3, 5, 1, 2, 3}]

{1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 5, 5, 6, 6, 6}

• I do not want random numbers of items. I want specific numbers of each item in the multiset. For example, in the case shown I want four 1's, two 2's, one 3, three 4's, no fives, two 6's, one 7, and three 8's. Oct 17 '13 at 18:14
• @TylerDurden ok :) see my edit
– Kuba
Oct 17 '13 at 19:35
Flatten[ Range[ 1, 9, {1, 1, 1}], {2, 1}]
Join @@ Table[ i, {i, 9}, {3}]
Join @@ Array[ # &, {9, 3}]
Table[ 1/9 (3 n - 2 Sqrt[3] Cos[(2 π n)/3 + π/6] + 3), {n, 27}]


{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9}

• This answer gets the booby prize, or the tongue-in-cheek prize, as the case may be. Oct 29 '13 at 16:45
• +1 for the first one; a nice way to use only two functions! Dec 29 '13 at 20:11

For your second question:

MapThread[ConstantArray, {Range[9], RandomInteger[{1, 3}, 9]}] // Flatten
(* {1, 1, 1, 2, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 9} *)


Yet another way, for the first part:

With[{n = 3, k = 10}, Quotient[Range[k n], n, -n+1]] (* incorporating Artes' suggestion *)


Updated:

For the second part, an approach not already covered in the other answers:

Module[{i = 1, reps = {4, 5, 1, 3}},
Fold[#1~Join~ConstantArray[ i++, #2] &, {}, reps]] (* reps are variable repetition factors *)


{1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 4, 4, 4}

• Very nice (+1) however I'd rather do this Quotient[ Range[3 9], 3, -2] to get exaclty what has been expected. Consider to answer another question in the OP. Dec 29 '13 at 22:33
• Oh sorry - for some reason I imagined the sequence began with zeros. I'll modify my answer.
– Aky
Dec 29 '13 at 22:42
• Why do you need With? There is no need for Range[1, k n] since it's equal to Range[k n]. And what about variable number of items? Dec 29 '13 at 22:50
• Oops, that was a mistake - I was really sleepy when I wrote that. Fixed now. The With is just for convenience and generalization, if someone wants to use the same method with a different range or repetition value. (I could've written it as a function.)
– Aky
Dec 30 '13 at 5:59