4

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]]]];
sortableTable[
  Table[
    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.

mikea
  • 379
  • 1
  • 5

4 Answers4

7

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

ybeltukov
  • 43,673
  • 5
  • 108
  • 212
5

There is no need to re-invent SortBy.

Stealing ybeltukov solution:

Dynamic@Grid@
 Prepend[data, 
         Array[Button[names[[#]],
                      data = SortBy[data, Function[x, #[[x]] &][#]]
                     ] &
               , cols]
        ]
Kuba
  • 136,707
  • 13
  • 279
  • 740
4

Here is a possibility:

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

DynamicModule[{idx = 1},
 Dynamic@Grid[
   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},
 Dynamic@Grid[
   Prepend[data[[Ordering[data[[All, idx]]]]], 
    MapIndexed[Setter[Dynamic@idx, #2, #1] &, labels]],
   Spacings -> {0, Automatic}]
 ]
Michael E2
  • 235,386
  • 17
  • 334
  • 747
1

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"};
lastSort=0;
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.

Trev
  • 763
  • 3
  • 8