Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Suppose I have a notebook with cells tagged with both numbers and words with numbers always list first, and some cells without cell tags e.g.:

Cell[ ..., CellTags->{"1","cat"}],
Cell[ ..., CellTags->{"2","dog"}],
Cell[ ...],
Cell[ ..., CellTags->{"7","cow"}],
}, notebook options ...]

What is the easiest way to sort the cells based on 1) cell tag numbers, i.e. the first element in the cell tag list; and 2) cell tag words, i.e. the second element in the cell tag list?

Note that there could be additional tags but the first and second tags are always number and single word. Note also there there is unlikely to be grouped cells (none at this stage), although a solution that took cell grouping into account and rendered a sorted ungrouped answer would be fine. I want a fast way to sort a notebook and render the newly sorted form. I'm thinking ideally I'd like a palette button for this.

share|improve this question
I am generally useless at this kind of programmatic Notebook manipulation. I appreciate questions like this because I learn a lot from the answers. –  Mr.Wizard Feb 23 '12 at 12:44
I might have another look at my answer today. I'm thinking that Cases is quite repetitive. Maybe just use it once on the content and then use Ordering would be more efficient. –  Mike Honeychurch Feb 23 '12 at 20:51
I definitely didn't read this comment before I wrote my answer, but that's effectively what I did. Crumbs! –  Martin John Hadley Nov 26 at 10:30

3 Answers 3

This is actually something I am working on for real at the moment. This is what i have got so far:

nb = NotebookOpen["path/to/notebook.nb"];
SelectionMove[nb, All, Notebook];
content = NotebookRead[nb];

For searching based on the number listed as the first tag:

sorted = SortBy[content, 
 Cases[#, HoldPattern[
     Rule[CellTags, {x_ /; StringMatchQ[x, NumberString], __}]] :> 
    ToExpression@x] &]

and for searching based on the word in the second tag:

sorted = SortBy[content, 
 Cases[#, HoldPattern[Rule[CellTags, {_, x_, __}]] :> x] &]

With both of these methods the untagged cells are returned first ...which is ok. Then

Scan[NotebookWrite[nb, #] &, sorted];

This is a little bit slow but seems to be doing the job.


So this code seems to be doing the job on the notebooks i needed to use this with. Would still be interested in suggestions from others.


Options[sortNotebook] = {"sortby" -> "Number"};

sortNotebook[file_String, OptionsPattern[]] := 
 Module[{nb, opt = OptionValue["sortby"], sorted},

  nb = NotebookOpen[file];
  SelectionMove[nb, All, Notebook];
  content = NotebookRead[nb];
   opt === "Number", 
   sorted = 
         Rule[CellTags, {x_ /; StringMatchQ[x, NumberString], __}]] :>
         ToExpression@x] &],
   opt === "Task", 
   sorted = 
     Cases[#, HoldPattern[Rule[CellTags, {_, x_, __}]] :> x] &],
   opt =!= "Number" && opt =!= "Task", 
   Print["you messed up with the option names"]
  Scan[NotebookWrite[nb, #] &, sorted];
share|improve this answer

I was searching for a way to reorder Sections alphabetically and found this question, which I think I've got an answer for.

It relies on evaluating the code in a separate notebook, targeting the notebook with your cell tags. In my target notebook (targetNb) I have the following cells:

Cell["Typed Cell 1", "Item", CellTags -> {"1", "Zebra", "2"}], 
Cell["Typed Cell 2", "Item", CellTags -> {"2", "cat", "3"}],
Cell["Typed Cell 3","Item", CellTags -> {"4", "bat", "6"}],
Cell["Typed Cell 4", "Item", CellTags -> {"4", "aardvark", "8"}]

I built the following function:

reOrderTaggedItems[nb_] := Block[{allCells = Cells[nb],targetCellTags,targetCells,orderedCells},
targetCellTags =Cases[NotebookRead /@ allCells, 
Cell[__, CellTags -> q : {_, word_, __}] :> q];
targetCells = allCells[[Flatten@Position[NotebookRead /@ allCells,Cell[__, CellTags -> {_, word_,__}]]]];
orderedCells =NotebookRead /@ targetCells[[Ordering[targetCellTags[[All, 2]]]]];
Table[NotebookWrite[targetCells[[i]], orderedCells[[i]]], {i, 4}]]

Applying the function reorders my cells:

Out[1]=Cell["Typed Cell 4", "Item", CellTags -> {"4", "aardvark", "8"}], 
Cell["Typed Cell 3", "Item", CellTags -> {"4", "bat", "6"}], 
Cell["Typed Cell 2", "Item", CellTags -> {"2", "cat", "3"}], 
Cell["Typed Cell 1", "Item", CellTags -> {"1", "Zebra", "2"}]

I'm currently looking into doing this for Section cells, but correctly handling the CellGroupData which will be slightly trickier.

share|improve this answer

My answer is based a on Martin John Hadley's. It uses Cells so one need at least V9 for that. But it is quite compact:

cellSortByTag[nb_, tagOp_: ToExpression] := Composition[
    (SelectionMove[#, Cell, All]; NotebookWrite[nb, #2]) & @@@ # &,
    { #, 
      NotebookRead /@ SortBy[#, tagOp @ CurrentValue[#, CellTags] &]} &


Now you can test it:

NB = Array[ Cell[BoxData[ToBoxes[#]], "Input", CellTags -> {ToString[11 - #]}] &,
          ] // CreateDocument[#, ShowCellTags -> True] &;

You can evaluate this even inside NB:

 cellSortByTag[ EvaluationNotebook[] ];

If outside, you have to put proper NotebookObject of course. Moreover it will only affect those tagged Cells so you may have whatever else you want there.

If you have more tags, just add second arg to handle sorting, e.g.:

cellSortByTag[EvaluationNotebook[], Last]
share|improve this answer
Composition, @@@, and arbitrary tag patterns??? Way to make a man feel lazy. Your solution even handles the annoying way that singular CellTags aren't put in Lists. +1 –  Martin John Hadley Nov 26 at 11:43
@MartinJohnHadley Unfortunately NotebookWrite alone is not enough. It works but it has some side effects, not obvious, due to incorrect usage. I had to add some things. –  Kuba Nov 29 at 13:52

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.