I'm trying to build a sortable grid:

sortBy[data_, idx_] := 
  If[idx == 0, data, Sort[data, #1[[idx]] < #2[[idx]] &]];
sortableTable[data_, header_] := DynamicModule[
  {h, sort = 0},
  h = MapIndexed[Button[#1, sort = #2] &, header];
  Dynamic[Grid[Prepend[sortBy[data, sort], h]]]];
    RandomInteger[100], {i, 3}, {j, 4}], 
    {"First", "Second", "Third", "Fourth"}]

For some reason it is not behaving like I expected: header disappears and sorting doesn't occur. I've tried all sorts of stuff but can't make it work like intended. Any help is much appreciated.

  • 3
    $\begingroup$ try: sort = First@#2 in the MapIndexed $\endgroup$ – Pinguin Dirk Sep 9 '13 at 17:54
  • $\begingroup$ Thanks. This was the actual error. $\endgroup$ – mikea Sep 9 '13 at 20:24

This should work:

rows = 3;
cols = 4;
data = RandomInteger[{1, 100}, {rows, cols}];
names = {"First", "Second", "Third", "Fourth"};
sortBy[data_, idx_] := If[idx == 0, data, Sort[data, #1[[idx]] < #2[[idx]] &]];
Dynamic@Grid@Prepend[data, Array[Button[names[[#]], data = sortBy[data, #]] &, cols]]

enter image description here


There is no need to re-invent SortBy.

Stealing ybeltukov solution:

                      data = SortBy[data, Function[x, #[[x]] &][#]]
                     ] &
               , cols]
  • $\begingroup$ You forgot to localize x. $\endgroup$ – Mr.Wizard Sep 9 '13 at 21:14
  • $\begingroup$ @Mr.Wizard I didn't, I was just too lazy to do this. Or frustrated by recent question where no one cared about localiziing t or letter case in sin till you posted the answer :) $\endgroup$ – Kuba Sep 9 '13 at 21:18
  • 1
    $\begingroup$ @Mr.Wizard But I should act like an adult so I've edited it :p $\endgroup$ – Kuba Sep 9 '13 at 21:32

Here is a possibility:

labels = {"First", "Second", "Third", "Fourth"};
nrows = 4;
data = RandomInteger[{1, 100}, {nrows, Length@labels}];

DynamicModule[{idx = 1},
   Prepend[SortBy[data, #[[idx]] &], 
    MapIndexed[Setter[Dynamic@idx, #2, #1] &, labels]],
   Spacings -> {0, Automatic}]

Mathematica graphics

If speed on a large dataset should happen to be an issue (unlikely, since the data are displayed), then this will be faster:

DynamicModule[{idx = 1},
   Prepend[data[[Ordering[data[[All, idx]]]]], 
    MapIndexed[Setter[Dynamic@idx, #2, #1] &, labels]],
   Spacings -> {0, Automatic}]

The above answers worked for me, but I thought I'd add my tweak to allow clicking twice to toggle reverse sort:

names={"matches","p1","p2","diff","factored","gap signature"};
disp:=Dynamic[Refresh[Block[{t},Grid[Prepend[tab,Array[Button[names[[#]],t=If[#==lastSort,lastSort=0;-1,lastSort=#;1];data=SortBy[data,Function[x,t #[[x]]&][#]]]&,Length[names]]]]],TrackedSymbols:>{data}]];

This gives the buttons the names provided, and disp is now a dynamic stand-in for the sortable table.

As an aside, the Refresh[expr, TrackedSymbols:>{data}] I've found seems to help Dynamic expressions not suck up so much CPU.


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.