Given the notebook itself we can think of it as a tree graph: the root vertex is the empty notebook itself, the headers of cell groups are vertices, and all other cells as leaf vertices.

I'd like to represent the notebook in this fashion for several reasons, the obvious one being TOC.

For example, given this notebook:

nb = CreateDocument[{TextCell["A", "Title"], 
        TextCell["B", "Section"],
        TextCell["C", "ItemNumbered"],
        TextCell["D", "Text"], TextCell["E", "Program"],
        TextCell["F", "Section"],
        TextCell["G", "ItemNumbered"]}];

the output would be:

TreePlot[{"A" -> "B", "A" -> "F", "F" -> "G", "B" -> "C", "B" -> "D", 
      "B" -> "E"}, VertexLabeling -> True]

enter image description here

So it would be easy to get a handle on "C" and know it's location in the tree is "A"->"B"->"C".

Right now I do this in a dumb manual way:

cells = Cells[nb, CellStyle -> {"ItemNumbered"}]

(*Out[66]= {CellObject[46385], CellObject[46401]}*)

c = cells[[1]] (*cell whose toc location I want*)

(*Out[67]= CellObject[46385]*)

out = Table[SelectionMove[c, Before, CellGroup, i];
  SelectionMove[nb, Next, Cell];
  {i, 2, 10(*a guess for max depth*)}]

(*Out[68]= {"B", "A", "A", "A", "A", "A", "A", "A", "A"}*)

   out, {x___, Repeated[out[[-1]]]} :> x, {0, \[Infinity]}], out[[-1]]]

(*Out[69]= {"A", "B"}*)


I've realized that in a real use case the contents of cells like input and program might be very complicated, and so it is necessary to use references in the form of CellObjects.

  • $\begingroup$ The difference is, Input and frieds are not exactly children for e.g. Section, they are grouped together. $\endgroup$ – Kuba Dec 2 '15 at 7:03
  • $\begingroup$ correct, but I'm saying let's treat them as children of the first cell in their group $\endgroup$ – M.R. Dec 2 '15 at 7:17
  • $\begingroup$ There is another problem, there are different types of grouping so I suppose you have to specify what do you want in each case. E.g. Items are grouped together so the first cell in this group isn't parent to the rest but sibling. $\endgroup$ – Kuba Dec 2 '15 at 7:30
  • $\begingroup$ For simplicity let's assume that the first cell in any group of any type is the parent $\endgroup$ – M.R. Dec 2 '15 at 15:01
  • $\begingroup$ And also, I'm only considering the standard CellGroupingRules -> Automatic in my notebooks (with various stylesheet override) but not GroupTogetherGrouping nor GroupTogetherNestedGrouping, both of which complicate things as you noted. $\endgroup$ – M.R. Dec 2 '15 at 17:37

Given an (open) notebook nb, let's read its structure:

(*In[2]:=*) cellStructure = NotebookGet[nb]

(*Out[2]=*) Notebook[{Cell[
   CellGroupData[{Cell["A", "Title"], 
     Cell[CellGroupData[{Cell["B", "Section"], 
        Cell["C", "ItemNumbered"], Cell["D", "Text"], 
        Cell["E", "Program"]}, Open]], 
     Cell[CellGroupData[{Cell["F", "Section"], 
        Cell["G", "ItemNumbered"]}, Open]]}, Open]]}, 
 WindowSize -> {808, 911}, 
 WindowMargins -> {{Automatic, 237}, {Automatic, 78}}, 
 FrontEndVersion -> "10.3 for Linux x86 (64-bit) (October 9, 2015)",
 StyleDefinitions -> "Default.nb"]

We can see that the grouped sections are all in a structure Cell[CellGroupData[{...}, Open]]. Use pattern matching to find the cell groups and remove the cell styling information meanwhile as well:

(*In[3]:=*) nested = 
 cellStructure[[1]] //.
  {Cell[CellGroupData[{first_, rest__}, ___]] :> (first -> {rest}), 
   Cell[contents_, rest___] :> contents}

(*Out[3]=*) {"A" -> {"B" -> {"C", "D", "E"}, "F" -> {"G"}}}

We now have this nested structure that we need to flatten out to get a list of edges. Here's one way of doing that:

(*In[4]:=*) flattened = 
 Flatten[nested //.
  (parent_ -> children : {__}) :>
    (If[MatchQ[#, _Rule], {parent -> First[#], #}, parent -> #] & /@ children)]

(*Out[4]=*) {"A" -> "B", "B" -> "C", "B" -> "D", "B" -> "E", "A" -> "F", 
 "F" -> "G"}

This output can then be fed to TreePlot or Graph.

| improve this answer | |
  • $\begingroup$ Nice, I like how this extracts the groupings graph, but in a real use case the contents of cells like input and program might be very complicated, it would be better to use references in the form of CellObjects, don't you think? $\endgroup$ – M.R. Dec 4 '15 at 21:31
  • $\begingroup$ @Kuba Right, just updated the question. $\endgroup$ – M.R. Dec 7 '15 at 14:17

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.