7
$\begingroup$

Could someone suggest some alternatives or improvements to the code below? What I wrote works, but there may be ways to accomplish this more elegantly with the various capabilities of the language.

The objective is that given a list of strings, assign an index number to each item and then arrange into columns, such that each column has no more than a given number of entries. Below are sample results. enter image description here

Below is the code that I wrote to do this.

makeTOC[nameList_List, nRowsIn_Integer: 5] := 
 Module[{nNames, nRows, nCols, m1B, m1A, r1AB, toc},
  nNames = Length@nameList;
  nRows = Min[nRowsIn, nNames];
  nCols = Quotient[nNames, nRows] + If[ Divisible[nNames, nRows], 0, 1];
  m1B = ArrayReshape[nameList, {nCols, nRows}, ""] // Transpose;
  m1A = ArrayReshape[Range@Length@nameList, {nCols, nRows}, ""] // Transpose;
  r1AB = Riffle[Flatten@m1A, Flatten@m1B];
  toc = Partition[r1AB, 2 nCols]  ;
  toc
  ]

There is probably a better way. One approach that didn't work, was to use ArrayReshape only once, by first combining the indices and names, and then calling ArrayReshape. For example,

makeTOCalt[nameList_List, nRowsIn_Integer: 5] := 
 Module[{nNames, nRows, nCols, r1AB, toc},
  nNames = Length@nameList;
  nRows = Min[nRowsIn, nNames];
  nCols = Quotient[nNames, nRows] + If[ Divisible[nNames, nRows], 0, 1];
  r1AB = Transpose[{Range[Length@testNames], testNames}] ;
  toc = ArrayReshape[r1AB, {nCols, nRows}];
  toc
  ]

The alternative code is more clear, because it directly combines the indices with the names and then operates on the result. But alas, it doesn't work.

Let me know if you see areas for improvement or alternatives. Thanks!

$\endgroup$
  • $\begingroup$ thanks everyone. Your answers provided new perspectives on how to use the language. $\endgroup$ – user6546 Oct 25 '17 at 4:40
7
$\begingroup$

Here is how I would do it:

makeTOC[names_, n_] := Flatten[
    Partition[
        Thread[{Range@Length@names, names}],
        n, n, 1, {}
    ],
    {{2}, {1, 3}}
]

And a few examples:

makeTOC[testNames, 3] //TableForm
makeTOC[testNames, 5] //TableForm
makeTOC[testNames, 8] //TableForm

enter image description here

$\endgroup$
  • $\begingroup$ Thanks! Initial testing suggests that the following are equivalent, but still parsing the documentation to understand if this is true for all cases. Partition[Thread[{Range@Length@names, names}], UpTo[n] ] Partition[Thread[{Range@Length@names, names}], n, n, 1, {}] $\endgroup$ – user6546 Oct 25 '17 at 4:57
  • $\begingroup$ @user6546 I think it is true, I'm just not used to using UpTo. $\endgroup$ – Carl Woll Oct 25 '17 at 4:59
4
$\begingroup$

one another possibility

make list of names

list = ("name_" <> ToString[#]) & /@ Range[13]

Mathematica graphics

Then use Partition with option UpTo and then use TableForm with Heading

nRows=4;
TableForm[Partition[list,UpTo[nRows]],TableHeadings->{Range[nRows],None}]

Mathematica graphics

$\endgroup$
2
$\begingroup$

Multicolumn is similar to what you are trying to do except that Multicolumn[list, cols] uses the smallest number of rows so that all elements will be shown. That is, for a given number of columns it balances the entries per column.

makeTOC[nameList_List, nRowsIn_Integer: 5] := 
 Multicolumn[testNames, Ceiling[Length[nameList]/nRowsIn]]

testNames = "Name_" <> # & /@ CharacterRange["A", "J"];

makeTOC[testNames, #] & /@ {3, 5, 8, 12} //
 Column[#, Spacings -> 2] &

enter image description here

$\endgroup$
  • $\begingroup$ Good to know about Multicolumn. Also, that function can be persuaded to work with rows rather than columns, So, that the following makes a table with the requested number of rows.: makeTOC[nameList_List, nRowsIn_Integer: 5] := Multicolumn[nameList, {Min[Length@nameList, nRowsIn], Automatic}] $\endgroup$ – user6546 Oct 25 '17 at 4:39
1
$\begingroup$

Here's an approach using ArrayReshape that puts the indices and the names in the same string, and then reshapes the result (like your second approach, I think).

makeTOC[namelist_, n_] := Module[
  {numberednames = 
    MapIndexed[ToString[First@#2] <> "  " <> #1 &, namelist]},
  Transpose@
    ArrayReshape[
     numberednames, {Ceiling[Length[namelist]/n], n}] /. {0 -> Nothing}
  ]

Some examples:

makeTOC[testNames, 3] // TableForm
makeTOC[testNames, 8] // TableForm

enter image description here

enter image description here

Putting the indices in a string with the names means that there are going to be string formatting issues to think about (like Name_J being out of alignment). As you know, keeping the indices out of the strings can complicate the structure of the table, and I can't really see a way around it that isn't just paraphrasing @CarlWoll's Flattening.

If for whatever reason you wanted to use ArrayReshape rather than Partition you could do

makeTOC[namelist_, n_] := Flatten[
  ArrayReshape[
    Transpose[{Range@Length@#, #} &@testNames], 
      {Ceiling[Length[testNames]/n], n, 2}] /. {0, 0} -> Nothing,
  {{2}, {1, 3}}]

which essentially recreates @CarlWoll's method in a more verbose way.

$\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.