TableAlignments -> Left not working

The Problem

I believe this is a bug in TableForm. We can see by looking at the Box form of the output that the option ColumnAlignments of the outermost GridBox does not behave as it should.

tab = {{1, {1, 1, 1}}, {2, {2, 2, 2}}, {3, {3, 3, 3}}, {4, {4, 4, 4}}};

getOption = Options[First@ToBoxes@#, ColumnAlignments] &;

tForm =
 TableForm[tab, 
   TableHeadings -> {None, {"Title 1", "Title 2"}}, 
   TableAlignments -> #] &;

Table[
  {ali, getOption @ tForm @ ali},
  {ali, {Left, Center, Right, Top, Bottom}}
] // TableForm

Mathematica graphics

As you can see this option tracks TableAlignments for every value except Left.


A solution

Since this answer is not complete without a work-around:

fix =
  DisplayForm @ Replace[
    ToBoxes @ #,
    (ca : ColumnAlignments -> _) :> (ca -> Left),
    {2}
  ] &;

fix @ tableImage

Mathematica graphics


A fix to load at startup

Here are two options for a fix suitable for inclusion in your init.m file.

Method #1

This is just packaging the simple fix above. It could be problematic because it forces evaluation of TableForm into boxes, when TableForm normally acts as a wrapper. For this reason method #2 should be used if possible.

Unprotect[TableForm];

x : TableForm[__, TableAlignments -> Left | {Left, _}, ___] :=
  Replace[
    MakeBoxes @ x,
    (ca : ColumnAlignments -> _) :> (ca -> Left),
    {2}
  ] // DisplayForm

Protect[TableForm];

Method #2

This is considerably more verbose, but it works at the correct level (MakeBoxes). However, it may be a bit fragile: when a TableForm expression is first displayed (or explicitly sent to MakeBoxes) certain additional definitions are loaded internally. I use such an explicit call, add my definition, and then modify the order of the FormatValues of TableForm. Should there be some other internal initialization that resets the FormatValues or changes their order this fix will be lost. It is important that this code not be evaluated twice in one session or the FormatValues will be out of order and the fix will fail.

Unprotect[TableForm]

TableForm[{}] // ToBoxes; (* pre-load TableForm's FormatValues; do not remove! *)

MakeBoxes[x : TableForm[__, opts : OptionsPattern[]], _] /; ! TrueQ[tfAlignLeftFix] :=
  Block[{tfAlignLeftFix = True},
    Replace[MakeBoxes@x, (ca : ColumnAlignments -> _) :> (ca -> Left), {2}]
  ] /; MatchQ[OptionValue[TableForm, {opts}, TableAlignments], Left | {Left, _}]

FormatValues@TableForm = RotateRight@FormatValues@TableForm;

Protect[TableForm]

This method also adds robust detection of the alignment option, e.g. including those set with Options[TableForm] = . . . and of the form TableForm[_, {options}].


For some reason, your nested data rows throw off the alignment. If you Column your second entries, you get this:

table[[All, 2]] = Column /@ table[[All, 2]];
TableForm[table, 
 TableHeadings -> {None, {"Dihedral", "\[Tau] per state [ns]"}}, 
 TableAlignments -> {Left}]

Correct output

Personally, I mostly switched over to Grid because it is more versatile, but of course TableForm is still supported and useful...


The following modification of your code gives what you expect:

TableForm[Map[TableForm[#, TableAlignments -> Left] &, table, {2}], 
TableHeadings -> {None, {"Dihedral", "\[Tau] per state [ns]"}}, 
TableAlignments -> Left]

enter image description here

EDIT: A variation applying Column to the second elements of the lists (as in @Yves's answer):

TableForm[Map[{First@#,Column @@ Rest[#]}&, table], 
TableHeadings -> {None, {"Dihedral", "\[Tau] per state [ns]"}}, 
TableAlignments -> Left]