Display an outline as a nested table: Difference between revisions
Display an outline as a nested table (view source)
Revision as of 16:10, 29 March 2024
, 2 months agoNew post.
SqrtNegInf (talk | contribs) m (→{{header|zkl}}: Fix comment: Perl 6 --> Raku) |
(New post.) |
||
(32 intermediate revisions by 10 users not shown) | |||
Line 1:
{{
{| class="wikitable" style="text-align: center;"
Line 80:
Display your nested table on this page.
<br>
=={{header|AutoHotkey}}==
<syntaxhighlight lang="autohotkey">outline2table(db, Delim:= "`t"){
oNum:=[], oMID:=[], oNod := [], oKid := [], oPnt := [], oMbr := [], oLvl := []
oCrl := ["#ffffe6;", "#ffebd2;", "#f0fff0;", "#e6ffff;", "#ffeeff;"]
col := 0, out := "", anc := ""
; create numerical index for each line
for i, line in StrSplit(db, "`n", "`r")
{
RegExMatch(line, "^(\t*)(.*)$", m)
out .= m1 . i "`n"
oNum[i] := m2
}
db := Trim(out, "`n")
; create list of members, parents, kids and their ancestors
for i, mbr in StrSplit(db, "`n", "`r")
{
lvl := 1
While (SubStr(mbr, 1, 1) = Delim)
lvl++, mbr := SubStr(mbr, 2)
if (pLvl >= lvl) && pMbr
col++
, oMbr[pLvl, pMbr] .= "col:" col ",anc:" anc
, oKid[pLvl, pMbr] .= "col:" col ",anc:" anc
if (pLvl > lvl) && pMbr
loop % pLvl - lvl
anc := RegExReplace(anc, "\d+_?$")
if (pLvl < lvl) && pMbr
anc .= pMbr "_"
, oMbr[pLvl, pMbr] .= "col:" col+1 ",anc:" anc
, oPnt[pLvl, pMbr] .= "col:" col+1 ",anc:" anc
pLvl := lvl
pMbr := mbr
;~ oMID[lvl] := TV_Add(mbr, oMID[lvl-1], "Expand")
}
; last one on the list
col++
oMbr[pLvl, pMbr] .= "col:" col ",anc:" anc
oKid[pLvl, pMbr] .= "col:" col ",anc:" anc
; setup node color
clr := 1
for lvl, obj in oMbr
for node, str in obj
if (lvl <= 2)
oNod[node, "clr"] := clr++
else
oNod[node, "clr"] := oNod[StrSplit(str, "_").2, "clr"]
; setup node level/column/width
for lvl, obj in oKid
for node, str in obj
{
x := StrSplit(str, ",")
col := StrReplace(x.1, "col:")
anc := Trim(StrReplace(x.2, "anc:"), "_")
for j, a in StrSplit(anc, "_")
oNod[a, "wid"] := (oNod[a, "wid"]?oNod[a, "wid"]:0) + 1
oNod[node, "lvl"] := lvl
oNod[node, "col"] := col
oNod[node, "wid"] := 1
}
for lvl, obj in oPnt
for node, str in obj
{
x := StrSplit(str, ",")
col := StrReplace(x.1, "col:")
anc := Trim(StrReplace(x.2, "anc:"), "_")
oNod[node, "lvl"] := lvl
oNod[node, "col"] := col
}
; setup members by level
for node, obj in oNod
oLvl[obj["lvl"], node] := 1
maxW := 0
for node in oLvl[1]
maxW += oNod[node, "wid"]
; setup HTML
html := "<table class=""wikitable"" style=""text-align: center;"">`n"
for lvl, obj in oLvl
{
pCol := 1
html .= "<tr>`n"
for node, bool in obj
{
while (oNod[node, "col"] <> pCol)
pCol++, html .= "`t<td style=""background: #F9F9F9;""></td>`n"
pCol += oNod[node, "wid"]
if !cNum := Mod(oNod[node, "clr"], 5)
cNum := 5
html .= "`t<td style=""background: " oCrl[cNum] """ colspan=""" oNod[node, "wid"] """>" oNum[node] "</td>`n"
}
while (pCOl <= maxW)
pCol++, html .= "`t<td style=""background: #F9F9F9;""></td>`n"
html .= "</tr>`n"
}
html .= "</table>"
; setup wikitable
wTable := "{| class=""wikitable"" style=""text-align: center;""`n"
for lvl, obj in oLvl
{
pCol := 1
wTable .= "|-`n"
for node, bool in obj
{
while (oNod[node, "col"] <> pCol)
pCol++, wTable .= "| | `n"
pCol += oNod[node, "wid"]
if !cNum := Mod(oNod[node, "clr"], 5)
cNum := 5
wTable .= "| style=""background: " oCrl[cNum] """ colspan=""" oNod[node, "wid"] " |" oNum[node] "`n"
}
while (pCOl <= maxW)
pCol++, wTable .= "| | `n"
}
wTable .= "|}`n"
return [html, wTable]
}
</syntaxhighlight>
Examples:<syntaxhighlight lang="autohotkey">db =
(
Display an outline as a nested table.
Parse the outline to a tree,
measuring the indent of each line,
translating the indentation to a nested structure,
and padding the tree to even depth.
count the leaves descending from each node,
defining the width of a leaf as 1,
and the width of a parent node as a sum.
(The sum of the widths of its children)
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.
)
Gui, add, ActiveX, vDocument w1000 r14, HTMLFile
result := outline2table(db)
Document.Write(result.1)
Gui, Show
MsgBox % "HTML:`n" result.1 "`n`nWikitable:`n" result.2
return</syntaxhighlight>
{{out}}
HTML:
<table class="wikitable" style="text-align: center;">
<tr>
<td style="background: #ffffe6;" colspan="7">Display an outline as a nested table.</td>
</tr>
<tr>
<td style="background: #ffebd2;" colspan="3">Parse the outline to a tree,</td>
<td style="background: #f0fff0;" colspan="2">count the leaves descending from each node,</td>
<td style="background: #e6ffff;" colspan="2">and write out a table with 'colspan' values</td>
</tr>
<tr>
<td style="background: #ffebd2;" colspan="1">measuring the indent of each line,</td>
<td style="background: #ffebd2;" colspan="1">translating the indentation to a nested structure,</td>
<td style="background: #ffebd2;" colspan="1">and padding the tree to even depth.</td>
<td style="background: #f0fff0;" colspan="1">defining the width of a leaf as 1,</td>
<td style="background: #f0fff0;" colspan="1">and the width of a parent node as a sum.</td>
<td style="background: #e6ffff;" colspan="1">either as a wiki table,</td>
<td style="background: #e6ffff;" colspan="1">or as HTML.</td>
</tr>
<tr>
<td style="background: #F9F9F9;"></td>
<td style="background: #F9F9F9;"></td>
<td style="background: #F9F9F9;"></td>
<td style="background: #F9F9F9;"></td>
<td style="background: #f0fff0;" colspan="1">(The sum of the widths of its children)</td>
<td style="background: #F9F9F9;"></td>
<td style="background: #F9F9F9;"></td>
</tr>
</table>
Wikitable:
{| class="wikitable" style="text-align: center;"
|-
| style="background: #ffffe6;" colspan="7 |Display an outline as a nested table.
|-
| style="background: #ffebd2;" colspan="3 |Parse the outline to a tree,
| style="background: #f0fff0;" colspan="2 |count the leaves descending from each node,
| style="background: #e6ffff;" colspan="2 |and write out a table with 'colspan' values
|-
| style="background: #ffebd2;" colspan="1 |measuring the indent of each line,
| style="background: #ffebd2;" colspan="1 |translating the indentation to a nested structure,
| style="background: #ffebd2;" colspan="1 |and padding the tree to even depth.
| style="background: #f0fff0;" colspan="1 |defining the width of a leaf as 1,
| style="background: #f0fff0;" colspan="1 |and the width of a parent node as a sum.
| style="background: #e6ffff;" colspan="1 |either as a wiki table,
| style="background: #e6ffff;" colspan="1 |or as HTML.
|-
| |
| |
| |
| |
| style="background: #f0fff0;" colspan="1 |(The sum of the widths of its children)
| |
| |
|}
=={{header|Go}}==
<
import (
Line 235 ⟶ 445:
toNest(iNodes2, 0, 0, &n2)
fmt.Println(toMarkup(n2, cols2, 4))
}</
{{out}}
Line 288 ⟶ 498:
| style="background: #ffebd2; " colspan=1 | (The sum of the widths of its children)
| style="background: #ffebd2; " colspan=1 | Propagating the sums upward as necessary.
| |
| |
|}
=={{header|Haskell}}==
<syntaxhighlight lang="haskell">{-# LANGUAGE TupleSections #-}
module OutlineTree where
import Data.Bifunctor (first)
import Data.Bool (bool)
import Data.Char (isSpace)
import Data.List (find, intercalate)
import Data.Tree (Tree (..), foldTree, levels)
---------------- NESTED TABLES FROM OUTLINE --------------
wikiTablesFromOutline :: [String] -> String -> String
wikiTablesFromOutline colorSwatch outline =
intercalate "\n\n" $
wikiTableFromTree colorSwatch
<$> ( forestFromLineIndents
. indentLevelsFromLines
. lines
)
outline
wikiTableFromTree :: [String] -> Tree String -> String
wikiTableFromTree colorSwatch =
wikiTableFromRows
. levels
. paintedTree colorSwatch
. widthLabelledTree
. (paddedTree "" <*> treeDepth)
--------------------------- TEST -------------------------
main :: IO ()
main =
( putStrLn
. wikiTablesFromOutline
[ "#ffffe6",
"#ffebd2",
"#f0fff0",
"#e6ffff",
"#ffeeff"
]
)
"Display an outline as a nested table.\n\
\ Parse the outline to a tree,\n\
\ measuring the indent of each line,\n\
\ translating the indentation to a nested structure,\n\
\ and padding the tree to even depth.\n\
\ count the leaves descending from each node,\n\
\ defining the width of a leaf as 1,\n\
\ and the width of a parent node as a sum.\n\
\ (The sum of the widths of its children)\n\
\ and write out a table with 'colspan' values\n\
\ either as a wiki table,\n\
\ or as HTML."
------------- TREE STRUCTURE FROM NESTED TEXT ------------
forestFromLineIndents :: [(Int, String)] -> [Tree String]
forestFromLineIndents = go
where
go [] = []
go ((n, s) : xs) =
let (subOutline, rest) = span ((n <) . fst) xs
in Node s (go subOutline) : go rest
indentLevelsFromLines :: [String] -> [(Int, String)]
indentLevelsFromLines xs =
let pairs = first length . span isSpace <$> xs
indentUnit = maybe 1 fst (find ((0 <) . fst) pairs)
in first (`div` indentUnit) <$> pairs
---------------- TREE PADDED TO EVEN DEPTH ---------------
paddedTree :: a -> Tree a -> Int -> Tree a
paddedTree padValue = go
where
go tree n
| 1 >= n = tree
| otherwise =
Node
(rootLabel tree)
( (`go` pred n)
<$> bool nest [Node padValue []] (null nest)
)
where
nest = subForest tree
treeDepth :: Tree a -> Int
treeDepth = foldTree go
where
go _ [] = 1
go _ xs = (succ . maximum) xs
----------------- SUBTREE WIDTHS MEASURED ----------------
widthLabelledTree :: Tree a -> Tree (a, Int)
widthLabelledTree = foldTree go
where
go x [] = Node (x, 1) []
go x xs =
Node
(x, foldr ((+) . snd . rootLabel) 0 xs)
xs
------------------- COLOR SWATCH APPLIED -----------------
paintedTree :: [String] -> Tree a -> Tree (String, a)
paintedTree [] tree = fmap ("",) tree
paintedTree (color : colors) tree =
Node
(color, rootLabel tree)
( zipWith
(fmap . (,))
(cycle colors)
(subForest tree)
)
-------------------- WIKITABLE RENDERED ------------------
wikiTableFromRows :: [[(String, (String, Int))]] -> String
wikiTableFromRows rows =
let wikiRow = unlines . fmap cellText
cellText (color, (txt, width))
| null txt = "| |"
| otherwise =
"| "
<> cw color width
<> "| "
<> txt
cw color width =
let go w
| 1 < w = " colspan=" <> show w
| otherwise = ""
in "style=\"background:"
<> color
<> "; \""
<> go width
<> " "
in "{| class=\"wikitable\" "
<> "style=\"text-align: center;\"\n|-\n"
<> intercalate "|-\n" (wikiRow <$> rows)
<> "|}"</syntaxhighlight>
{{Out}}
{| class="wikitable" style="text-align: center;"
|-
| style="background:#ffffe6; " colspan=7 | Display an outline as a nested table.
|-
| style="background:#ffebd2; " colspan=3 | Parse the outline to a tree,
| style="background:#f0fff0; " colspan=2 | count the leaves descending from each node,
| style="background:#e6ffff; " colspan=2 | and write out a table with 'colspan' values
|-
| style="background:#ffebd2; " | measuring the indent of each line,
| style="background:#ffebd2; " | translating the indentation to a nested structure,
| style="background:#ffebd2; " | and padding the tree to even depth.
| style="background:#f0fff0; " | defining the width of a leaf as 1,
| style="background:#f0fff0; " | and the width of a parent node as a sum.
| style="background:#e6ffff; " | either as a wiki table,
| style="background:#e6ffff; " | or as HTML.
|-
| |
| |
| |
| |
| style="background:#f0fff0; " | (The sum of the widths of its children)
| |
| |
|}
=={{header|J}}==
Implementation:
<syntaxhighlight lang="j">depth=: (i.~ ~.)@(0 i."1~' '=];._2)
tree=: (i: 0>.<:@{:)\
width=: {{NB. y is tree
c=. *i.#y NB. children
NB. sum of children, inductively
y (+//. c&*)`(~.@[)`]}^:_ c
}}
NB. avoid dark colors
NB. avoid dark colors
NB. avoid dark colors
pastel=: {{256#.192+?y$,:3#64}}
task=: {{
depths=: depth y NB. outline structure
t=: tree depths NB. outline as tree
pad=: (i.#depths) -. t,I.(=>./)depths
tr=: t,pad NB. outline as constant depth tree
dr=: depths,1+pad{depths
lines=:(#dr){.<@dlb;._2 y
widths=. width tr NB. column widths
top=. I.2>dr
color=.<"1 hfd 8421504 (I.tr e.pad)} (top top} tr)&{^:_ (<:2^24),pastel<:#dr
r=.'{| class="wikitable" style="text-align: center;"',LF
for_d.~.dr do. NB. descend through the depths
k=.I.d=dr NB. all lines at this depth
p=. |:({:,~{&tr)^:d ,:k
j=. k/:p NB. order padding to fit parents
r=. r,'|-',LF
r=. r,;'| style="background: #',L:0 (j{color),L:0'" colspan=',L:0(j{widths),&":each' | ',L:0 (j{lines),L:0 LF
end.
r=.r,'|}',LF
}}</syntaxhighlight>
Given the task example outline:
<syntaxhighlight lang="j">outline=:{{)n
Display an outline as a nested table.
Parse the outline to a tree,
measuring the indent of each line,
translating the indentation to a nested structure,
and padding the tree to even depth.
count the leaves descending from each node,
defining the width of a leaf as 1,
and the width of a parent node as a sum.
(The sum of the widths of its children)
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.
}}</syntaxhighlight>
Generated output from <tt>task outline</tt> was:
{| class="wikitable" style="text-align: center;"
|-
| style="background: #ffffff" colspan=7 | Display an outline as a nested table.
|-
| style="background: #e7d7cd" colspan=3 | Parse the outline to a tree,
| style="background: #e9c5d1" colspan=2 | count the leaves descending from each node,
| style="background: #d8e9f4" colspan=2 | and write out a table with 'colspan' values
|-
| style="background: #e7d7cd" colspan=1 | measuring the indent of each line,
| style="background: #e7d7cd" colspan=1 | translating the indentation to a nested structure,
| style="background: #e7d7cd" colspan=1 | and padding the tree to even depth.
| style="background: #e9c5d1" colspan=1 | defining the width of a leaf as 1,
| style="background: #e9c5d1" colspan=1 | and the width of a parent node as a sum.
| style="background: #d8e9f4" colspan=1 | either as a wiki table,
| style="background: #d8e9f4" colspan=1 | or as HTML.
|-
| style="background: #808080" colspan=1 |
| style="background: #808080" colspan=1 |
| style="background: #808080" colspan=1 |
| style="background: #808080" colspan=1 |
| style="background: #e9c5d1" colspan=1 | (The sum of the widths of its children)
| style="background: #808080" colspan=1 |
| style="background: #808080" colspan=1 |
|}
=={{header|Java}}==
<syntaxhighlight lang="java">
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public final class DisplayAnOutlineAsANestedTable {
public static void main(String[] args) {
String outline = """
Display an outline as a nested table.
Parse the outline to a tree,
measuring the indent of each line,
translating the indentation to a nested structure,
and padding the tree to even depth.
count the leaves descending from each node,
defining the width of a leaf as 1,
and the width of a parent node as a sum.
(The sum of the widths of its children)
Propagating the sums upward as necessary.
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.
Optionally add color to the nodes.
""";
Node tree = parse(outline);
colourTree(tree);
String htmlCode = htmlTable(tree);
System.out.println(htmlCode);
String wikiCode = wikiTable(tree);
System.out.println(wikiCode);
}
// Return the HTML code for the display of the given Node as a table.
private static String htmlTable(Node tree) {
final int tableColumnCount = tree.colspan();
int rowColumn = 0;
StringBuilder builder = new StringBuilder("<table style='text-align: center;' >\n");
// Breadth first traversal of 'tree'.
Deque<Node> queue = new ArrayDeque<Node>();
Set<Node> explored = new HashSet<Node>();
queue.offer(tree);
while ( ! queue.isEmpty() ) {
Node currentNode = queue.poll();
if ( explored.contains(currentNode) ) {
continue;
}
if ( rowColumn == 0 ) {
builder.append(" <tr>\n");
}
builder.append(htmlTableData(currentNode));
rowColumn += currentNode.colspan();
if ( rowColumn == tableColumnCount ) {
builder.append(" </tr>\n");
rowColumn = 0;
}
for ( Node child : currentNode.children ) {
queue.offer(child);
}
explored.add(currentNode);
}
builder.append("</table>\n");
return builder.toString();
}
// Return the code for the display of the given Node as a table in Wikipedia.
private static String wikiTable(Node tree) {
final int tableColumnCount = tree.colspan();
int rowColumn = 0;
StringBuilder builder = new StringBuilder();
builder.append("{| class=\"" + "wikitable" + "\"" + " style=\"" + "text-align: center;" + "\"" + "\n");
// Breadth first traversal of 'tree'.
Deque<Node> queue = new ArrayDeque<Node>();
Set<Node> explored = new HashSet<Node>();
queue.offer(tree);
while ( ! queue.isEmpty() ) {
Node currentNode = queue.poll();
if ( explored.contains(currentNode) ) {
continue;
}
if ( rowColumn == 0 ) {
builder.append("|-\n");
}
builder.append(wikiTableData(currentNode));
rowColumn += currentNode.colspan();
if ( rowColumn == tableColumnCount ) {
rowColumn = 0;
}
for ( Node child : currentNode.children ) {
queue.offer(child);
}
explored.add(currentNode);
}
builder.append("|}\n");
return builder.toString();
}
// Return an HTML table data element for the given Node.
private static String htmlTableData(Node node) {
String indent = " ";
String colspan = " colspan=\"" + node.colspan() + "\"";
String style = "style=\"" + "background-color: " + node.colour + "\"";
String attributes = colspan + " " + style;
return indent + "<td" + attributes + " >" + node.text + "</td>\n";
}
// Return a Wikipedia table data element for the given Node.
private static String wikiTableData(Node node) {
if ( node.text.isBlank() ) {
return "| |\n";
}
String style = "style=\"" + "background: " + node.colour + " \"";
String colspan = " colspan=" + node.colspan();
String attributes = style + colspan;
return "| " + attributes + " | " + node.text + "\n";
}
// Return the given outline as a tree of Node.
private static Node parse(String outline) {
List<Token> tokens = tokenise(outline);
Node temporaryTree = new Node("", -1, null);
parse(tokens, 0, temporaryTree);
Node tree = temporaryTree.children.getFirst();
padTree(tree, tree.height());
return tree;
}
// Recursively build a tree of Node.
private static void parse(List<Token> tokens, int index, Node node) {
if ( index == tokens.size() ) {
return;
}
Token token = tokens.get(index);
if ( token.indent == node.indent ) { // A sibling of node
Node current = new Node(token.text, token.indent, node.parent);
node.parent.children.addLast(current);
parse(tokens, index + 1, current);
} else if ( token.indent > node.indent ) { // A child of node
Node current = new Node(token.text, token.indent, node);
node.children.addLast(current);
parse(tokens, index + 1, current);
} else if ( token.indent < node.indent ) { // Try the node's parent until a sibling is found
parse(tokens, index, node.parent);
}
}
// Pad the tree with blank nodes so that all branches have the same depth.
private static void padTree(Node node, int height) {
if ( node.isLeaf() && node.depth() < height ) {
Node padNode = new Node("", node.indent + 1, node);
node.children.addLast(padNode);
}
for ( Node child : node.children ) {
padTree(child, height);
}
}
private static void colourTree(Node node) {
if ( node.text.isBlank() ) {
node.colour = Colour.blank();
} else if ( node.depth() <= 1 ) {
node.colour = Colour.next();
} else {
node.colour = node.parent.colour;
}
for ( Node child : node.children ) {
colourTree(child);
}
}
private static List<Token> tokenise(String outline) {
List<Token> tokens = new ArrayList<Token>();
for ( String line : outline.split("\n") ) {
String lineTrimmed = line.trim();
final int indent = line.length() - lineTrimmed.length();
tokens.addLast( new Token(lineTrimmed, indent) );
}
return tokens;
}
private static final class Node {
public Node (String aText, int aIndent, Node aParent) {
text = aText;
indent = aIndent;
parent = aParent;
children = new ArrayList<Node>();
}
public int depth() {
return ( parent != null ) ? parent.depth() + 1 : -1;
}
public int height() {
if ( isLeaf() ) {
return 0;
}
return children.stream().map( child -> child.height() ).max(Comparator.naturalOrder()).get() + 1;
}
public int colspan() {
if ( isLeaf() ) {
return 1;
}
return children.stream().map( child -> child.colspan() ).mapToInt(Integer::intValue).sum();
}
public boolean isLeaf() {
return children.isEmpty();
}
private String text;
private int indent;
private Node parent;
private List<Node> children;
private String colour;
}
private static final class Colour {
public static String next() {
index = ( index + 1 ) % colours.size();
return colours.get(index);
}
public static String blank() {
return "#cccccc;";
}
private static int index = -1;
private static final List<String> colours = List.of( "#ffff66;", "#ffcc66;", "#ccffcc;", "#ccccff;",
"#ffcccc;", "#00cccc;", "#cc9966;", "#ffccff;" );
}
private record Token(String text, int indent) { }
}
</syntaxhighlight>
{{ out }}
HTML Table
<table style='text-align: center;' >
<tr>
<td colspan="9" style="background-color: #ffff66;" >Display an outline as a nested table.</td>
</tr>
<tr>
<td colspan="3" style="background-color: #ffcc66;" >Parse the outline to a tree,</td>
<td colspan="3" style="background-color: #ccffcc;" >count the leaves descending from each node,</td>
<td colspan="2" style="background-color: #ccccff;" >and write out a table with 'colspan' values</td>
<td colspan="1" style="background-color: #ffcccc;" >Optionally add color to the nodes.</td>
</tr>
<tr>
<td colspan="1" style="background-color: #ffcc66;" >measuring the indent of each line,</td>
<td colspan="1" style="background-color: #ffcc66;" >translating the indentation to a nested structure,</td>
<td colspan="1" style="background-color: #ffcc66;" >and padding the tree to even depth.</td>
<td colspan="1" style="background-color: #ccffcc;" >defining the width of a leaf as 1,</td>
<td colspan="2" style="background-color: #ccffcc;" >and the width of a parent node as a sum.</td>
<td colspan="1" style="background-color: #ccccff;" >either as a wiki table,</td>
<td colspan="1" style="background-color: #ccccff;" >or as HTML.</td>
<td colspan="1" style="background-color: #cccccc;" ></td>
</tr>
<tr>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #ccffcc;" >(The sum of the widths of its children)</td>
<td colspan="1" style="background-color: #ccffcc;" >Propagating the sums upward as necessary.</td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
</tr>
</table>
Wiki Table
{| class="wikitable" style="text-align: center;"
|-
| style="background: #ffff66; " colspan=9 | Display an outline as a nested table.
|-
| style="background: #ffcc66; " colspan=3 | Parse the outline to a tree,
| style="background: #ccffcc; " colspan=3 | count the leaves descending from each node,
| style="background: #ccccff; " colspan=2 | and write out a table with 'colspan' values
| style="background: #ffcccc; " colspan=1 | Optionally add color to the nodes.
|-
| style="background: #ffcc66; " colspan=1 | measuring the indent of each line,
| style="background: #ffcc66; " colspan=1 | translating the indentation to a nested structure,
| style="background: #ffcc66; " colspan=1 | and padding the tree to even depth.
| style="background: #ccffcc; " colspan=1 | defining the width of a leaf as 1,
| style="background: #ccffcc; " colspan=2 | and the width of a parent node as a sum.
| style="background: #ccccff; " colspan=1 | either as a wiki table,
| style="background: #ccccff; " colspan=1 | or as HTML.
| |
|-
| |
| |
| |
| |
| style="background: #ccffcc; " colspan=1 | (The sum of the widths of its children)
| style="background: #ccffcc; " colspan=1 | Propagating the sums upward as necessary.
| |
| |
| |
Line 293 ⟶ 1,102:
=={{header|JavaScript}}==
<
// ----------- NESTED TABLES FROM OUTLINE ------------
// wikiTablesFromOutline :: [String] -> String -> String
const wikiTablesFromOutline = colorSwatch =>
outline => forestFromIndentedLines(
indentLevelsFromLines(lines(outline))
)
.map(wikiTableFromTree(colorSwatch))
.join("\n\n");
// wikiTableFromTree :: [String] -> Tree String -> String
const wikiTableFromTree = colorSwatch =>
compose(
wikiTableFromRows,
levels,
paintedTree(colorSwatch),
widthLabelledTree,
ap(paddedTree(""))(treeDepth)
);
// ---------------------- TEST -----------------------
// main :: IO ()
const main = () => {
Line 309 ⟶ 1,140:
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.`;
"#e6ffff",
};
//
//
// [Tree String]
const forestFromIndentedLines = tuples => {
const [indented, body] = Array.from(
xs[0]
),
[tree, rest] = Array.from(
span(compose(lt(indented), fst))(
tail(xs)
)
);
// followed by the rest.
return [
return go(tuples);
};
// indentLevelsFromLines :: [String] -> [(Int, String)]
const indentLevelsFromLines = xs => {
const
span(isSpace)(list(x))
indentUnit =
i < a ? i : a
) : a;
},
Infinity
);
return [Infinity, 0].includes(indentUnit) ? (
pairs
) : pairs.map(first(n => n / indentUnit));
};
// ------------ TREE PADDED TO EVEN DEPTH ------------
// paddedTree :: a
const paddedTree = padValue =>
// with empty nodes
node =>
const go = n
return Node(root(tree))(
(
0 < children.length ? (
children
) : [Node(padValue)([])]
).map(go(n - 1))
);
})() : tree;
return go(depth)(node);
};
// treeDepth :: Tree a -> Int
const treeDepth = tree =>
foldTree(
() => xs => 0 < xs.length ? (
1 + maximum(xs)
) : 1
)(tree);
// ------------- SUBTREE WIDTHS MEASURED -------------
// widthLabelledTree :: Tree a -> Tree (a, Int)
const widthLabelledTree = tree =>
// A tree in which each node is labelled with
// the width of its own subtree.
foldTree(x => xs =>
0 < xs.length ? (
Node(Tuple(x)(
xs.reduce(
(a, node) => a + snd(root(node)),
0
)
))(xs)
) : Node(Tuple(x)(1))([])
)(tree);
// -------------- COLOR SWATCH APPLIED ---------------
// paintedTree :: [String] -> Tree a -> Tree (String, a)
const paintedTree = colorSwatch =>
tree => 0 < colorSwatch.length ? (
Node(
Tuple(colorSwatch[0])(root(tree))
)(
zipWith(compose(fmapTree, Tuple))(
cycle(colorSwatch.slice(1))
)(
nest(tree)
)
)
) : fmapTree(Tuple(""))(tree);
// --------------- WIKITABLE RENDERED ----------------
// wikiTableFromRows ::
// [[(String, (String, Int))]] -> String
const wikiTableFromRows = rows => {
const
cw = color => width => {
const go = w =>
1 < w ? (
`colspan=${w} `
) : "";
return `style="background:${color}; "` + (
` ${go(width)}`
);
cellText = ctw =>
const [color, tw] = Array.
return 0 < txt.length ? (
`| ${cw(color)(width)}| ${txt}`
) : "| |";
},
classText = "class=\"wikitable\"",
styleText = "style=\"text-align:center;\"",
header = `{| ${classText} ${styleText}\n|-`,
tableBody = rows.map(
cells => cells.map(cellText).join("\n")
).join("\n|-\n");
return `${header}\n${tableBody}\n|}`;
};
// ------------------ GENERIC TREES ------------------
const Node = v =>
// value of some
// more child
xs =>
type:
root:
nest: xs ||
// fmapTree :: (a -> b) -> Tree a -> Tree
const fmapTree = f => {
// structure-preserving application of f
// to each root in the existing tree.
const go = t => Node(
f(t.root)
)(
t.nest.map(go)
);
return go;
};
//
const foldTree = f => {
// The catamorphism on trees. A summary
// value obtained by a depth-first fold.
const go = tree
);
return go;
};
// levels :: Tree a -> [[a]]
const levels = tree
// A list of lists, grouping the root
// values of each level of the tree.
const [h, ...t] = 0 < a.length ? (
) : [
[],
[]
];
return [
[node.root, ...h],
...node.nest.slice(0)
.reverse()
.reduce(go, t)
];
};
return go([], tree);
};
// nest :: Tree a -> [a]
const nest = tree => {
// Allowing for lazy (on-demand) evaluation.
// If the nest turns out to be a function –
// rather than a list – that function is applied
// here to the root, and returns a list.
const xs = tree.nest;
};
// root :: Tree a -> a
const root = tree =>
// The value attached to a tree node.
tree.root;
// --------------------- GENERIC ---------------------
// Just :: a -> Maybe a
const Just = x => ({
type: "Maybe",
Nothing: false,
Just: x
});
const Nothing = ()
});
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a =>
b => ({
type: "Tuple",
"0": a,
"1": b,
length: 2
});
// apFn :: (a -> b -> c) -> (a -> b) -> (a -> c)
const ap = f =>
// Applicative instance for functions.
// f(x) applied to g(x).
g => x => f(x)(
g(x)
);
// bimap :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)
const bimap = f =>
// Tuple instance of bimap.
// A tuple of the application of f and g to the
// first and second values respectively.
g => tpl => Tuple(f(tpl[0]))(
g(tpl[1])
);
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
// composition of all the functions in fs.
fs.reduce(
(f, g) => x => f(g(x)),
x => x
);
// cycle :: [a] -> Generator [a]
const cycle = function*
// An infinite repetition of xs,
// from which an arbitrary prefix
// may be taken.
const lng = xs.length;
let i = 0;
while (true) {
yield
i = (1 + i) % lng;
}
};
//
// A simple function lifted to one which applies
// to a tuple, transforming only its first item.
xy =>
const tpl = Tuple(f(xy[0]))(xy[1]);
Array.from(tpl)
// fst :: (a, b) -> a
const fst = tpl =>
// First member of a pair.
tpl[0];
// isSpace :: Char -> Bool
const isSpace = c =>
// True if c is a white space character.
(/\s/u).test(c);
// length :: [a] -> Int
const length = xs =>
// Returns Infinity over objects without finite
// length. This enables zip and zipWith to choose
// the shorter argument when one is non-finite,
// like cycle, repeat etc
"GeneratorFunction" !== xs.constructor
.constructor.name ? (
xs.length
) : Infinity;
// lines :: String -> [String]
const lines = s =>
// A list of strings derived from a single
// string delimited by newline and or CR.
0 < s.length ? (
s.split(/[\r\n]+/u)
) : [];
const list =
// xs itself, if it is an Array,
// or an Array derived from xs.
Array.isArray(xs) ? (
xs
) :
//
const
//
const
(a, y) => y > a ? (
y
) : a, ys[0]
)
) : undefined
)(list(xs));
//
const
// Second member of a pair.
tpl[1];
// span :: (a -> Bool) -> [a] -> ([a], [a])
const span = p =>
// all satisfy p, tupled with the remainder of xs.
const i = xs.findIndex(x
) : Tuple(xs)([]);
};
// tail :: [a] -> [a]
const tail = xs =>
// A new list consisting of all
// items of xs except the first.
"GeneratorFunction" !== xs.constructor
.constructor.name ? (
(ys => 0 < ys.length ? ys.slice(1) : [])(
xs
)
) : (take(1)(xs), xs);
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n
// The first n elements of a list,
// string of characters, or stream.
xs => "GeneratorFunction" !== xs
.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
//
const
// Just a tuple of the head of xs and its tail,
// Or Nothing if xs is an empty list.
const lng = length(xs);
// Lazy generator
const nxt = take(1)(xs);
return 0 < nxt.length ? (
Just(Tuple(nxt[0])(xs))
) : Nothing();
})()
) : Nothing();
};
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f
// A list with the length of the shorter of
// xs and
// default tuple constructor.
xs => ys => {
const n = Math.min(length(xs), length(ys));
return Infinity > n ? (
(([as, bs]) => Array.from({
length: n
}, (_, i) => f(as[i])(
bs[i]
)))([xs, ys].map(
take(n)
))
) : zipWithGen(f)(xs)(ys);
};
// zipWithGen :: (a -> b -> c) ->
// Gen [a] -> Gen [b] -> Gen [c]
const zipWithGen = f => ga => gb => {
const go = function* (ma, mb) {
let
a = ma,
b = mb;
while (!a.Nothing && !b.Nothing) {
const
ta = a.Just,
tb = b.Just;
yield f(fst(ta))(fst(tb));
a = uncons(snd(ta));
b = uncons(snd(tb));
}
};
return go(uncons(ga), uncons(gb));
};
// MAIN ---
return main();
})();</
{{Out}}
{| class="wikitable" style="text-align:
|-
| style="background:
|-
| style="background:
| style="background:
| style="background:
|-
| style="background:
| style="background:
| style="background:
| style="background:
| style="background:
| style="background:
| style="background:
|-
|
|
|
|
| style="background:
|
|
|}
=={{header|Julia}}==
<
text = """
Line 844 ⟶ 1,752:
textplus = text * " Optionally add color to the nodes."
htmlfromdataframe(processtable(textplus))
</
<h4>A Rosetta Code Nested Table</h4><table style="width:100%" class="wikitable" >
<tr>
Line 916 ⟶ 1,824:
style = "background: #ddffdd;" >(The sum of the widths of its children)</td>
</tr>
</table>
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">s = "Display an outline as a nested table.
Parse the outline to a tree,
measuring the indent of each line,
translating the indentation to a nested structure,
and padding the tree to even depth.
count the leaves descending from each node,
defining the width of a leaf as 1,
and the width of a parent node as a sum.
(The sum of the widths of its children)
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.";
s = StringSplit[s, "\n"];
indentation = LengthWhile[Characters[#], EqualTo[" "]] & /@ s;
s = MapThread[StringDrop, {s, indentation}];
indentation =
indentation /.
Thread[Union[indentation] -> Range[Length[Union[indentation]]]];
ii = Transpose[{Range[Length[indentation]], indentation}];
(*ii//Grid*)
sel = Table[
{i, Last@Select[ii, #[[2]] < i[[2]] \[And] #[[1]] < i[[1]] &]}
,
{i, Rest@ii}
];
g = Graph[Rule @@@ sel[[All, All, 1]], VertexLabels -> "Name"];
vl = VertexList[g];
head = FirstPosition[vl, 1][[1]];
dm = GraphDistanceMatrix[g];
depth = ReverseSortBy[Transpose[{vl, dm[[All, head]]}], Last];
colspandb = <||>;
data = Table[
vert = d[[1]];
vd = VertexInDegree[g, vert];
vics = VertexInComponent[g, vert, {1}];
vocs = Rest@VertexOutComponent[g, vert];
cspan = 0;
Do[
If[KeyExistsQ[colspandb, vic],
cspan += colspandb[vic]
]
,
{vic, vics}
];
If[cspan == 0, cspan = 1];
AssociateTo[colspandb, d[[1]] -> cspan];
{Sequence @@ d, vd, vics, vocs, cspan}
,
{d, depth}
];
emptybefore = Table[
{d[[1]],
Length@
Select[
data, #[[1]] < d[[1]] \[And]
Length[#[[4]]] == 0 \[And] #[[2]] < d[[2]] &][[All, {1, 2,
3}]]}
,
{d, data}
];
emptybefore = Association[Rule @@@ emptybefore];
depthcopy = depth;
depthcopy[[All, 2]] += 1;
graphelements =
SortBy[Sort /@ GatherBy[depthcopy, Last], First /* Last][[All, All,
1]];
str = {"<table style='text-align: center;'>"};
colorsdb = <|1 -> "#ffffe6", 2 -> "#ffebd2", 6 -> "#f0fff0",
10 -> "#e6ffff"|>;
Do[
AppendTo[str, "<tr>"];
totalspan = 0;
Do[
If[KeyExistsQ[colorsdb, g],
color = colorsdb[g]
,
(*Print["sel",SelectFirst[data,First/*EqualTo[g]][[5]]];*)
color =
colorsdb[
Max[
Intersection[SelectFirst[data, First /* EqualTo[g]][[5]],
Keys[colorsdb]]]]
];
span = SelectFirst[data, First /* EqualTo[g]][[6]];
totalspan += span;
empty = emptybefore[g];
str = str~Join~
ConstantArray["<td style=\"background-color: #F9F9F9;\"></td>",
empty];
If[span == 1,
AppendTo[str,
"<td style=\"background-color: " <> color <> ";\">" <> s[[g]] <>
"</td>"];
,
AppendTo[str,
"<tdcolspan=\"" <> ToString[span] <>
"\" style=\"background-color: " <> color <> ";\">" <> s[[g]] <>
"</td>"];
];
,
{g, ge}
];
extra =
SelectFirst[data, First /* EqualTo[1]][[6]] - totalspan - empty;
str = str~Join~
ConstantArray["<td style=\"background-color: #F9F9F9;\"></td>",
extra];
AppendTo[str, "</tr>"];
,
{ge, graphelements}
]
AppendTo[str, "</table>"];
StringRiffle[str, "\n"]</syntaxhighlight>
{{out}}
<pre><table style='text-align: center;'>
<tr>
<tdcolspan="7" style="background-color: #ffffe6;">Display an outline as a nested table.</td>
</tr>
<tr>
<tdcolspan="3" style="background-color: #ffebd2;">Parse the outline to a tree,</td>
<tdcolspan="2" style="background-color: #f0fff0;">count the leaves descending from each node,</td>
<tdcolspan="2" style="background-color: #e6ffff;">and write out a table with 'colspan' values</td>
</tr>
<tr>
<td style="background-color: #ffebd2;">measuring the indent of each line,</td>
<td style="background-color: #ffebd2;">translating the indentation to a nested structure,</td>
<td style="background-color: #ffebd2;">and padding the tree to even depth.</td>
<td style="background-color: #f0fff0;">defining the width of a leaf as 1,</td>
<td style="background-color: #f0fff0;">and the width of a parent node as a sum.</td>
<td style="background-color: #e6ffff;">either as a wiki table,</td>
<td style="background-color: #e6ffff;">or as HTML.</td>
</tr>
<tr>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #f0fff0;">(The sum of the widths of its children)</td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
</tr>
</table></pre>
=={{header|Nim}}==
<syntaxhighlight lang="nim">import strutils
const Outline = """Display an outline as a nested table.
Parse the outline to a tree,
measuring the indent of each line,
translating the indentation to a nested structure,
and padding the tree to even depth.
count the leaves descending from each node,
defining the width of a leaf as 1,
and the width of a parent node as a sum.
(The sum of the widths of its children)
and write out a table with 'colspan' values
either as a wiki table,
or as HTML."""
type Color {.pure.} = enum
NoColor
Yellow = "#ffffe6;"
Orange = "#ffebd2;"
Green = "#f0fff0;"
Blue = "#e6ffff;"
const Line1Color = Yellow
const Line2Colors = [Orange, Green, Blue]
type Node = ref object
value: string
level: Natural
width: Natural
color: Color
parent: Node
children: seq[Node]
#---------------------------------------------------------------------------------------------------
proc leadingSpaces(line: string): int =
## return the number of leading spaces.
while line[result] == ' ':
inc result
#---------------------------------------------------------------------------------------------------
proc buildTree(outline: string): tuple[root: Node, depth: Natural] =
## Build the tree for the given outline.
result.root = Node()
var level: int
var startPos = @[-1]
var nodes: seq[Node] = @[result.root]
var linecount = 0
for line in Outline.splitLines:
inc linecount
if line.len == 0: continue
let start = line.leadingSpaces()
level = startPos.find(start)
if level < 0:
# Level not yet encountered.
if start < startPos[^1]:
raise newException(ValueError, "wrong indentation at line " & $linecount)
startPos.add(start)
nodes.add(nil)
level = startPos.high
# Create the node.
let node = Node(value: line.strip(), level: level)
let parent = nodes[level - 1]
parent.children.add(node)
node.parent = parent
nodes[level] = node # Set the node as current node for this level.
result.depth = nodes.high
#---------------------------------------------------------------------------------------------------
proc padTree(node: Node; depth: Natural) =
## pad the tree with empty nodes to get an even depth.
if node.level == depth:
return
if node.children.len == 0:
# Add an empty node.
node.children.add(Node(level: node.level + 1, parent: node))
for child in node.children:
child.padTree(depth)
#---------------------------------------------------------------------------------------------------
proc computeWidths(node: Node) =
## Compute the widths.
var width = 0
if node.children.len == 0:
width = 1
else:
for child in node.children:
child.computeWidths()
inc width, child.width
node.width = width
#---------------------------------------------------------------------------------------------------
proc build(nodelists: var seq[seq[Node]]; node: Node) =
## Build the list of nodes per level.
nodelists[node.level].add(node)
for child in node.children:
nodelists.build(child)
#---------------------------------------------------------------------------------------------------
proc setColors(nodelists: seq[seq[Node]]) =
## Set the colors of the nodes.
for node in nodelists[1]:
node.color = Line1Color
for i, node in nodelists[2]:
node.color = Line2Colors[i mod Line2Colors.len]
for level in 3..nodelists.high:
for node in nodelists[level]:
node.color = if node.value.len != 0: node.parent.color else: NoColor
#---------------------------------------------------------------------------------------------------
proc writeWikiTable(nodelists: seq[seq[Node]]) =
## Output the wikitable.
echo "{| class='wikitable' style='text-align: center;'"
for level in 1..nodelists.high:
echo "|-"
for node in nodelists[level]:
if node.width > 1:
# Node with children.
echo "| style='background: $1 ' colspan=$2 | $3".format(node.color, node.width, node.value)
elif node.value.len > 0:
# Leaf with contents.
echo "| style='background: $1 ' | $2".format(node.color, node.value)
else:
# Empty cell.
echo "| | "
echo "|}"
#---------------------------------------------------------------------------------------------------
proc writeHtml(nodelists: seq[seq[Node]]) =
## Output the HTML.
echo "<table class='wikitable' style='text-align: center;'>"
for level in 1..nodelists.high:
echo " <tr>"
for node in nodelists[level]:
if node.width > 1:
# Node with children.
echo " <td colspan='$1' style='background-color: $2'>$3</td>".format(node.width, node.color, node.value)
elif node.value.len > 0:
# Leaf with contents.
echo " <td style='background-color: $1'>$2</td>".format(node.color, node.value)
else:
# Empty cell.
echo " <td></td>"
echo " </tr>"
echo "</table>"
#———————————————————————————————————————————————————————————————————————————————————————————————————
let (root, depth) = Outline.buildTree()
root.padTree(depth)
root.computeWidths()
var nodelists = newSeq[seq[Node]](depth + 1)
nodelists.build(root)
nodelists.setColors()
echo "WikiTable:"
nodelists.writeWikiTable()
echo "HTML:"
nodelists.writeHtml()</syntaxhighlight>
{{out}}
WikiTable:
{| class='wikitable' style='text-align: center;'
|-
| style='background: #ffffe6; ' colspan=7 | Display an outline as a nested table.
|-
| style='background: #ffebd2; ' colspan=3 | Parse the outline to a tree,
| style='background: #f0fff0; ' colspan=2 | count the leaves descending from each node,
| style='background: #e6ffff; ' colspan=2 | and write out a table with 'colspan' values
|-
| style='background: #ffebd2; ' | measuring the indent of each line,
| style='background: #ffebd2; ' | translating the indentation to a nested structure,
| style='background: #ffebd2; ' | and padding the tree to even depth.
| style='background: #f0fff0; ' | defining the width of a leaf as 1,
| style='background: #f0fff0; ' | and the width of a parent node as a sum.
| style='background: #e6ffff; ' | either as a wiki table,
| style='background: #e6ffff; ' | or as HTML.
|-
| |
| |
| |
| |
| style='background: #f0fff0; ' | (The sum of the widths of its children)
| |
| |
|}
HTML:
<table class='wikitable' style='text-align: center;'>
<tr>
<td colspan='7' style='background-color: #ffffe6;'>Display an outline as a nested table.</td>
</tr>
<tr>
<td colspan='3' style='background-color: #ffebd2;'>Parse the outline to a tree,</td>
<td colspan='2' style='background-color: #f0fff0;'>count the leaves descending from each node,</td>
<td colspan='2' style='background-color: #e6ffff;'>and write out a table with 'colspan' values</td>
</tr>
<tr>
<td style='background-color: #ffebd2;'>measuring the indent of each line,</td>
<td style='background-color: #ffebd2;'>translating the indentation to a nested structure,</td>
<td style='background-color: #ffebd2;'>and padding the tree to even depth.</td>
<td style='background-color: #f0fff0;'>defining the width of a leaf as 1,</td>
<td style='background-color: #f0fff0;'>and the width of a parent node as a sum.</td>
<td style='background-color: #e6ffff;'>either as a wiki table,</td>
<td style='background-color: #e6ffff;'>or as HTML.</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td style='background-color: #f0fff0;'>(The sum of the widths of its children)</td>
<td></td>
<td></td>
</tr>
</table>
=={{header|Perl}}==
<
use strict;
Line 973 ⟶ 2,261:
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.</
{{out}}
<table border=1 cellspacing=0>
Line 1,006 ⟶ 2,294:
=={{header|Phix}}==
Can output in either html or wikitable markup
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">html</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">outlines</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"""
Display an outline as a nested table.
translating the indentation to a nested structure,
defining the
and
(The sum of the widths of its children)
and write out
or as HTML."""</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"""
Display an outline as a nested table.
translating the indentation to a nested structure,
defining the
and the width
(The sum of the widths of its children)
Propagating the sums upward as necessary.
and write out
either as a wiki table,
or as HTML.
Optionally add color to the nodes."""</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">yellow</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#ffffe6;"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">orange</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#ffebd2;"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">green</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#f0fff0;"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">blue</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#e6ffff;"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">pink</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#ffeeff;"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">colours</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">yellow</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">orange</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">green</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">blue</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pink</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">blue</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">yellow</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">orange</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">green</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pink</span><span style="color: #0000FF;">}}</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">calc_spans</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">ldx</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">children</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ldx</span><span style="color: #0000FF;">][$]</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">children</span><span style="color: #0000FF;">)!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">span</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">children</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">child</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">children</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">lines</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">calc_spans</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">,</span><span style="color: #000000;">child</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">span</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">child</span><span style="color: #0000FF;">][</span><span style="color: #000000;">4</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ldx</span><span style="color: #0000FF;">][</span><span style="color: #000000;">4</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">span</span>
<span style="color: #000080;font-style:italic;">-- else -- (span already 1)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">lines</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">markup</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">outline</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">colours</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">lines</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">outline</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">no_empty</span><span style="color: #0000FF;">:=</span><span style="color: #004600;">true</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span> <span style="color: #000080;font-style:italic;">-- indents (to locate parents)</span>
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span> <span style="color: #000080;font-style:italic;">-- indexes for ""</span>
<span style="color: #000000;">children</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">text</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">maxdepth</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">span</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">line</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">trim_tail</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #000000;">text</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">trim_head</span><span style="color: #0000FF;">(</span><span style="color: #000000;">line</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">indent</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">line</span><span style="color: #0000FF;">)-</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">text</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- remove any completed parents</span>
<span style="color: #008080;">while</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">and</span> <span style="color: #000000;">indent</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">[$]</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pi</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pdx</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pdx</span><span style="color: #0000FF;">[$]</span>
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][$]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][$])</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">i</span> <span style="color: #000080;font-style:italic;">-- (update children)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">indent</span>
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">i</span>
<span style="color: #000000;">depth</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">span</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> <span style="color: #000080;font-style:italic;">-- (default/assume no children[=={}])</span>
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">depth</span><span style="color: #0000FF;">,</span><span style="color: #000000;">indent</span><span style="color: #0000FF;">,</span><span style="color: #000000;">span</span><span style="color: #0000FF;">,</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span><span style="color: #000000;">text</span><span style="color: #0000FF;">,</span><span style="color: #000000;">children</span><span style="color: #0000FF;">}</span>
<span style="color: #000000;">maxdepth</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">max</span><span style="color: #0000FF;">(</span><span style="color: #000000;">maxdepth</span><span style="color: #0000FF;">,</span><span style="color: #000000;">depth</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">lines</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">calc_spans</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"<table class=\"wikitable\" style=\"text-align: center;\">\n"</span>
<span style="color: #0000FF;">:</span><span style="color: #008000;">"{| class=\"wikitable\" style=\"text-align: center;\"\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">d</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">maxdepth</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"<tr>\n"</span>
<span style="color: #0000FF;">:</span><span style="color: #008000;">"|-\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">cdx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">lii</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">lident</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">lii</span><span style="color: #0000FF;">,</span><span style="color: #000000;">depth</span><span style="color: #0000FF;">,</span><span style="color: #000000;">lident</span><span style="color: #0000FF;">,</span><span style="color: #000000;">span</span><span style="color: #0000FF;">,</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span><span style="color: #000000;">text</span><span style="color: #0000FF;">,</span><span style="color: #000000;">children</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">then</span> <span style="color: #000000;">cdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">style</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">`style="background: %s"`</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">colours</span><span style="color: #0000FF;">[</span><span style="color: #000000;">cdx</span><span style="color: #0000FF;">]})</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;">=</span><span style="color: #000000;">d</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">span</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span> <span style="color: #000000;">style</span> <span style="color: #0000FF;">&=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">` colspan="%d"`</span><span style="color: #0000FF;">,</span><span style="color: #000000;">span</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"<td %s>%s</td>\n"</span>
<span style="color: #0000FF;">:</span><span style="color: #008000;">"| %s | %s\n"</span><span style="color: #0000FF;">),{</span><span style="color: #000000;">style</span><span style="color: #0000FF;">,</span><span style="color: #000000;">text</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;"><</span><span style="color: #000000;">d</span> <span style="color: #008080;">and</span> <span style="color: #000000;">children</span><span style="color: #0000FF;">={}</span> <span style="color: #008080;">then</span>
<span style="color: #000080;font-style:italic;">-- res &=
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"<td %s></td>\n"</span>
<span style="color: #0000FF;">:</span><span style="color: #008000;">"| %s |\n"</span><span style="color: #0000FF;">),{</span><span style="color: #000000;">style</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">html</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">"</tr>\n"</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"</table>\n"</span>
<span style="color: #0000FF;">:</span><span style="color: #008000;">"|}\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">res</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">outlines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">markup</span><span style="color: #0000FF;">(</span><span style="color: #000000;">outlines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">colours</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
in html:
Line 1,237 ⟶ 2,528:
| style="background: #f0fff0;" |
| style="background: #ffeeff;" |
|}
=={{header|Python}}==
===Python: Procedural===
<syntaxhighlight lang="python">"""Display an outline as a nested table. Requires Python >=3.6."""
import itertools
import re
import sys
from collections import deque
from typing import NamedTuple
RE_OUTLINE = re.compile(r"^((?: |\t)*)(.+)$", re.M)
COLORS = itertools.cycle(
[
"#ffffe6",
"#ffebd2",
"#f0fff0",
"#e6ffff",
"#ffeeff",
]
)
class Node:
def __init__(self, indent, value, parent, children=None):
self.indent = indent
self.value = value
self.parent = parent
self.children = children or []
self.color = None
def depth(self):
if self.parent:
return self.parent.depth() + 1
return -1
def height(self):
"""Height of the subtree rooted at this node."""
if not self.children:
return 0
return max(child.height() for child in self.children) + 1
def colspan(self):
if self.leaf:
return 1
return sum(child.colspan() for child in self.children)
@property
def leaf(self):
return not bool(self.children)
def __iter__(self):
# Level order tree traversal.
q = deque()
q.append(self)
while q:
node = q.popleft()
yield node
q.extend(node.children)
class Token(NamedTuple):
indent: int
value: str
def tokenize(outline):
"""Generate ``Token``s from the given outline."""
for match in RE_OUTLINE.finditer(outline):
indent, value = match.groups()
yield Token(len(indent), value)
def parse(outline):
"""Return the given outline as a tree of ``Node``s."""
# Split the outline into lines and count the level of indentation.
tokens = list(tokenize(outline))
# Parse the tokens into a tree of nodes.
temp_root = Node(-1, "", None)
_parse(tokens, 0, temp_root)
# Pad the tree so that all branches have the same depth.
root = temp_root.children[0]
pad_tree(root, root.height())
return root
def _parse(tokens, index, node):
"""Recursively build a tree of nodes.
Args:
tokens (list): A collection of ``Token``s.
index (int): Index of the current token.
node (Node): Potential parent or sibling node.
"""
# Base case. No more lines.
if index >= len(tokens):
return
token = tokens[index]
if token.indent == node.indent:
# A sibling of node
current = Node(token.indent, token.value, node.parent)
node.parent.children.append(current)
_parse(tokens, index + 1, current)
elif token.indent > node.indent:
# A child of node
current = Node(token.indent, token.value, node)
node.children.append(current)
_parse(tokens, index + 1, current)
elif token.indent < node.indent:
# Try the node's parent until we find a sibling.
_parse(tokens, index, node.parent)
def pad_tree(node, height):
"""Pad the tree with blank nodes so all branches have the same depth."""
if node.leaf and node.depth() < height:
pad_node = Node(node.indent + 1, "", node)
node.children.append(pad_node)
for child in node.children:
pad_tree(child, height)
def color_tree(node):
"""Walk the tree and color each node as we go."""
if not node.value:
node.color = "#F9F9F9"
elif node.depth() <= 1:
node.color = next(COLORS)
else:
node.color = node.parent.color
for child in node.children:
color_tree(child)
def table_data(node):
"""Return an HTML table data element for the given node."""
indent = " "
if node.colspan() > 1:
colspan = f'colspan="{node.colspan()}"'
else:
colspan = ""
if node.color:
style = f'style="background-color: {node.color};"'
else:
style = ""
attrs = " ".join([colspan, style])
return f"{indent}<td{attrs}>{node.value}</td>"
def html_table(tree):
"""Return the tree as an HTML table."""
# Number of columns in the table.
table_cols = tree.colspan()
# Running count of columns in the current row.
row_cols = 0
# HTML buffer
buf = ["<table style='text-align: center;'>"]
# Breadth first iteration.
for node in tree:
if row_cols == 0:
buf.append(" <tr>")
buf.append(table_data(node))
row_cols += node.colspan()
if row_cols == table_cols:
buf.append(" </tr>")
row_cols = 0
buf.append("</table>")
return "\n".join(buf)
def wiki_table_data(node):
"""Return an wiki table data string for the given node."""
if not node.value:
return "| |"
if node.colspan() > 1:
colspan = f"colspan={node.colspan()}"
else:
colspan = ""
if node.color:
style = f'style="background: {node.color};"'
else:
style = ""
attrs = " ".join([colspan, style])
return f"| {attrs} | {node.value}"
def wiki_table(tree):
"""Return the tree as a wiki table."""
# Number of columns in the table.
table_cols = tree.colspan()
# Running count of columns in the current row.
row_cols = 0
# HTML buffer
buf = ['{| class="wikitable" style="text-align: center;"']
for node in tree:
if row_cols == 0:
buf.append("|-")
buf.append(wiki_table_data(node))
row_cols += node.colspan()
if row_cols == table_cols:
row_cols = 0
buf.append("|}")
return "\n".join(buf)
def example(table_format="wiki"):
"""Write an example table to stdout in either HTML or Wiki format."""
outline = (
"Display an outline as a nested table.\n"
" Parse the outline to a tree,\n"
" measuring the indent of each line,\n"
" translating the indentation to a nested structure,\n"
" and padding the tree to even depth.\n"
" count the leaves descending from each node,\n"
" defining the width of a leaf as 1,\n"
" and the width of a parent node as a sum.\n"
" (The sum of the widths of its children)\n"
" and write out a table with 'colspan' values\n"
" either as a wiki table,\n"
" or as HTML."
)
tree = parse(outline)
color_tree(tree)
if table_format == "wiki":
print(wiki_table(tree))
else:
print(html_table(tree))
if __name__ == "__main__":
args = sys.argv[1:]
if len(args) == 1:
table_format = args[0]
else:
table_format = "wiki"
example(table_format)</syntaxhighlight>
{{out}}
Wiki table
{| class="wikitable" style="text-align: center;"
|-
| colspan=7 style="background: #ffffe6;" | Display an outline as a nested table.
|-
| colspan=3 style="background: #ffebd2;" | Parse the outline to a tree,
| colspan=2 style="background: #f0fff0;" | count the leaves descending from each node,
| colspan=2 style="background: #e6ffff;" | and write out a table with 'colspan' values
|-
| style="background: #ffebd2;" | measuring the indent of each line,
| style="background: #ffebd2;" | translating the indentation to a nested structure,
| style="background: #ffebd2;" | and padding the tree to even depth.
| style="background: #f0fff0;" | defining the width of a leaf as 1,
| style="background: #f0fff0;" | and the width of a parent node as a sum.
| style="background: #e6ffff;" | either as a wiki table,
| style="background: #e6ffff;" | or as HTML.
|-
| |
| |
| |
| |
| style="background: #f0fff0;" | (The sum of the widths of its children)
| |
| |
|}
HTML table
<pre><table style='text-align: center;'>
<tr>
<tdcolspan="7" style="background-color: #ffffe6;">Display an outline as a nested table.</td>
</tr>
<tr>
<tdcolspan="3" style="background-color: #ffebd2;">Parse the outline to a tree,</td>
<tdcolspan="2" style="background-color: #f0fff0;">count the leaves descending from each node,</td>
<tdcolspan="2" style="background-color: #e6ffff;">and write out a table with 'colspan' values</td>
</tr>
<tr>
<td style="background-color: #ffebd2;">measuring the indent of each line,</td>
<td style="background-color: #ffebd2;">translating the indentation to a nested structure,</td>
<td style="background-color: #ffebd2;">and padding the tree to even depth.</td>
<td style="background-color: #f0fff0;">defining the width of a leaf as 1,</td>
<td style="background-color: #f0fff0;">and the width of a parent node as a sum.</td>
<td style="background-color: #e6ffff;">either as a wiki table,</td>
<td style="background-color: #e6ffff;">or as HTML.</td>
</tr>
<tr>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #f0fff0;">(The sum of the widths of its children)</td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
</tr>
</table></pre>
===Python: Functional===
<syntaxhighlight lang="python">'''Display an outline as a nested table'''
from itertools import chain, cycle, takewhile
from functools import reduce
from operator import add
# wikiTablesFromOutline :: [String] -> String -> String
def wikiTablesFromOutline(colorSwatch):
'''Wikitable markup for (colspan) tables representing
the indentation of a given outline.
Each key-line point (child of a tree root) has a
distinct color, inherited by all its descendants.
The first color in the swatch is for the root node.
A sequence of tables is generated where the outline
represents a forest rather than a singly-rooted tree.
'''
def go(outline):
return '\n\n'.join([
wikiTableFromTree(colorSwatch)(tree) for tree in
forestFromLevels(
indentLevelsFromLines(
outline.splitlines()
)
)
])
return go
# wikiTableFromTree :: [String] -> Tree String -> String
def wikiTableFromTree(colorSwatch):
'''A wikitable rendered from a single tree.
'''
return compose(
wikiTableFromRows,
levels,
paintedTree(colorSwatch),
widthMeasuredTree,
ap(paddedTree(""))(treeDepth)
)
# ------------------------- TEST -------------------------
# main :: IO ()
def main():
'''A colored wikitable rendering of a given outline'''
outline = '''Display an outline as a nested table.
Parse the outline to a tree,
measuring the indent of each line,
translating the indentation to a nested structure,
and padding the tree to even depth.
count the leaves descending from each node,
defining the width of a leaf as 1,
and the width of a parent node as a sum.
(The sum of the widths of its children)
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.'''
print(
wikiTablesFromOutline([
"#ffffe6",
"#ffebd2",
"#f0fff0",
"#e6ffff",
"#ffeeff"
])(outline)
)
# ------------------ TREE FROM OUTLINE -------------------
# indentLevelsFromLines :: [String] -> [(Int, String)]
def indentLevelsFromLines(xs):
'''Each input line stripped of leading
white space, and tupled with a preceding integer
giving its level of indentation from 0 upwards.
'''
indentTextPairs = [
(n, s[n:]) for (n, s)
in (
(len(list(takewhile(isSpace, x))), x)
for x in xs
)
]
indentUnit = len(next(
x for x in indentTextPairs if x[0]
)) or 1
return [
(x[0] // indentUnit, x[1])
for x in indentTextPairs
]
# forestFromLevels :: [(Int, String)] -> [Tree a]
def forestFromLevels(levelValuePairs):
'''A list of trees derived from a list of values paired
with integers giving their levels of indentation.
'''
def go(xs):
if xs:
level, v = xs[0]
children, rest = span(
lambda x: level < x[0]
)(xs[1:])
return [Node(v)(go(children))] + go(rest)
else:
return []
return go(levelValuePairs)
# -------------- TREE PADDED TO EVEN DEPTH ---------------
# paddedTree :: a -> (Int, Node a) -> Node a
def paddedTree(padValue):
'''A tree vertically padded to a given depth,
with additional nodes, containing padValue,
where needed.
'''
def go(tree):
def pad(n):
prev = n - 1
return Node(tree.get('root'))([
go(x)(prev) for x in (
tree.get('nest') or [Node(padValue)([])]
)
]) if prev else tree
return pad
return go
# treeDepth :: Tree a -> Int
def treeDepth(tree):
'''Maximum number of distinct levels in the tree.
'''
def go(_, xs):
return 1 + max(xs) if xs else 1
return foldTree(go)(tree)
# ------------ SPANNING WIDTH OF EACH SUBTREE ------------
# widthMeasuredTree :: Tree a -> Tree (a, Int)
def widthMeasuredTree(tree):
'''A tree in which each node value is tupled
with the width of the subtree.
'''
def go(x, xs):
return Node((x, 1))([]) if not xs else (
Node((x, reduce(
lambda a, child: a + (
child.get('root')[1]
),
xs,
0
)))(xs)
)
return foldTree(go)(tree)
# ----------------- COLOR SWATCH APPLIED -----------------
# paintedTree :: [String] -> Tree a -> Tree (String, a)
def paintedTree(swatch):
'''A tree in which every node value is tupled with
a hexadecimal color string taken from a swatch list.
The first colour is used for the root node.
The next n colours paint the root's n children.
All descendants of those children are painted with
the same color as their non-root ancestor.
'''
colors = cycle(swatch)
def go(tree):
return fmapTree(
lambda x: ("", x)
)(tree) if not swatch else (
Node(
(next(colors), tree.get('root'))
)(
list(map(
lambda k, child: fmapTree(
lambda v: (k, v)
)(child),
colors,
tree.get('nest')
))
)
)
return go
# ---------------- GENERIC TREE FUNCTIONS ----------------
# Node :: a -> [Tree a] -> Tree a
def Node(v):
'''Constructor for a Tree node which connects a
value of some kind to a list of zero or
more child trees.
'''
return lambda xs: {'root': v, 'nest': xs}
# fmapTree :: (a -> b) -> Tree a -> Tree b
def fmapTree(f):
'''A new tree holding the results of
an application of f to each root in
the existing tree.
'''
def go(x):
return Node(
f(x.get('root'))
)([go(v) for v in x.get('nest')])
return go
# foldTree :: (a -> [b] -> b) -> Tree a -> b
def foldTree(f):
'''The catamorphism on trees. A summary
value defined by a depth-first fold.
'''
def go(node):
return f(
node.get('root'),
[go(x) for x in node.get('nest')]
)
return go
# levels :: Tree a -> [[a]]
def levels(tree):
'''A list of lists, grouping the root
values of each level of the tree.
'''
return [[tree.get('root')]] + list(
reduce(
zipWithLong(add),
map(levels, tree.get('nest')),
[]
)
)
# ----------------- WIKITABLE RENDERING ------------------
# wikiTableFromRows :: [[(String, (String, Int))]] -> String
def wikiTableFromRows(rows):
'''A wiki table rendering of rows in which each cell
has the form (hexColorString, (text, colspan))
'''
def cw(color, width):
def go(w):
return f' colspan={w}' if 1 < w else ''
return f'style="background: {color}; "{go(width)}'
def cellText(cell):
color, (txt, width) = cell
return f'| {cw(color,width) if txt else ""} | {txt}'
def go(row):
return '\n'.join([cellText(cell) for cell in row])
return '{| class="wikitable" ' + (
'style="text-align: center;"\n|-\n'
) + '\n|-\n'.join([go(row) for row in rows]) + '\n|}'
# ----------------------- GENERIC ------------------------
# ap :: (a -> b -> c) -> (a -> b) -> a -> c
def ap(f):
'''Applicative instance for functions.
'''
def go(g):
return lambda x: f(x)(g(x))
return go
# compose :: ((a -> a), ...) -> (a -> a)
def compose(*fs):
'''Composition, from right to left,
of a series of functions.
'''
def go(f, g):
def fg(x):
return f(g(x))
return fg
return reduce(go, fs, lambda x: x)
# head :: [a] -> a
def head(xs):
'''The first element of a non-empty list.
'''
return xs[0] if isinstance(xs, list) else next(xs)
# isSpace :: Char -> Bool
# isSpace :: String -> Bool
def isSpace(s):
'''True if s is not empty, and
contains only white space.
'''
return s.isspace()
# span :: (a -> Bool) -> [a] -> ([a], [a])
def span(p):
'''The longest (possibly empty) prefix of xs that
contains only elements satisfying p, tupled with the
remainder of xs. span p xs is equivalent to
(takeWhile p xs, dropWhile p xs).
'''
def match(ab):
b = ab[1]
return not b or not p(b[0])
def f(ab):
a, b = ab
return a + [b[0]], b[1:]
def go(xs):
return until(match)(f)(([], xs))
return go
# until :: (a -> Bool) -> (a -> a) -> a -> a
def until(p):
'''The result of repeatedly applying f until p holds.
The initial seed value is x.
'''
def go(f):
def g(x):
v = x
while not p(v):
v = f(v)
return v
return g
return go
# zipWithLong :: ((a, a) -> a) -> ([a], [a]) -> [a]
def zipWithLong(f):
'''Analogous to map(f, xs, ys)
but returns a list with the length of the *longer*
of xs and ys, taking any surplus values unmodified.
'''
def go(xs, ys):
lxs = list(xs)
lys = list(ys)
i = min(len(lxs), len(lys))
return chain.from_iterable([
map(f, lxs, lys),
lxs[i:],
lys[i:]
])
return go
# MAIN ---
if __name__ == '__main__':
main()</syntaxhighlight>
{{Out}}
{| class="wikitable" style="text-align: center;"
|-
| style="background: #ffffe6; " colspan=7 | Display an outline as a nested table.
|-
| style="background: #ffebd2; " colspan=3 | Parse the outline to a tree,
| style="background: #f0fff0; " colspan=2 | count the leaves descending from each node,
| style="background: #e6ffff; " colspan=2 | and write out a table with 'colspan' values
|-
| style="background: #ffebd2; " | measuring the indent of each line,
| style="background: #ffebd2; " | translating the indentation to a nested structure,
| style="background: #ffebd2; " | and padding the tree to even depth.
| style="background: #f0fff0; " | defining the width of a leaf as 1,
| style="background: #f0fff0; " | and the width of a parent node as a sum.
| style="background: #e6ffff; " | either as a wiki table,
| style="background: #e6ffff; " | or as HTML.
|-
| |
| |
| |
| |
| style="background: #f0fff0; " | (The sum of the widths of its children)
| |
| |
|}
Line 1,246 ⟶ 3,259:
Strictly speaking, this is not a nested table. It is just a single level table that has some column spans > 1. For an example of using actual nested tables, see the task entry: [[Rosetta_Code/List_authors_of_task_descriptions#Raku|List_authors_of_task_descriptions#Raku]], [[Rosetta_Code/List_authors_of_task_descriptions/Full_list|(and full output)]].
<syntaxhighlight lang="raku"
Display an outline as a nested table.
Parse the outline to a tree,
Line 1,305 ⟶ 3,318:
@width.push: @width[*-1] * 10;
++$depth;
@array.map:
--$depth;
@width.pop;
Line 1,369 ⟶ 3,382:
$depth++;
my $list = "<ol style=\"list-style: {@type[$depth - 1]};\">\n" ~
( @array.map(
$depth--;
$list
Line 1,390 ⟶ 3,403:
}
( $r, $g, $b ).map( ((*+$m) * 255).Int)».base(16).join
}</
{{out}}
Line 1,453 ⟶ 3,466:
</ol>
</div>
=={{header|Wren}}==
{{trans|Go}}
{{libheader|Wren-dynamic}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./dynamic" for Struct
import "./fmt" for Fmt
var NNode = Struct.create("NNode", ["name", "children"])
var INode = Struct.create("INode", ["level", "name"])
var toNest // recursive function
toNest = Fn.new { |iNodes, start, level, n|
if (level == 0) n.name = iNodes[0].name
var i = start + 1
while (i < iNodes.count) {
if (iNodes[i].level == level+1) {
var c = NNode.new(iNodes[i].name, [])
toNest.call(iNodes, i, level+1, c)
n.children.add(c)
} else if (iNodes[i].level <= level) {
return
}
i = i + 1
}
}
var makeIndent = Fn.new { |outline, tab|
var lines = outline.split("\n")
var iNodes = List.filled(lines.count, null)
var i = 0
for (line in lines) {
var line2 = line.trimStart(" ")
var le = line.count
var le2 = line2.count
var level = ((le - le2) / tab).floor
iNodes[i] = INode.new(level, line2)
i = i + 1
}
return iNodes
}
var toMarkup = Fn.new { |n, cols, depth|
var span = 0
var colSpan // recursive closure
colSpan = Fn.new { |nn|
var i = 0
for (c in nn.children) {
if (i > 0) span = span + 1
colSpan.call(c)
i = i + 1
}
}
for (c in n.children) {
span = 1
colSpan.call(c)
}
var lines = []
lines.add("{| class=\"wikitable\" style=\"text-align: center;\"")
var l1 = "|-"
var l2 = "| |"
lines.add(l1)
span = 1
colSpan.call(n)
var s = Fmt.swrite("| style=\"background: $s \" colSpan=$d | $s", cols[0], span, n.name)
lines.add(s)
lines.add(l1)
var nestedFor // recursive function
nestedFor = Fn.new { |nn, level, maxLevel, col|
if (level == 1 && maxLevel > level) {
var i = 0
for (c in nn.children) {
nestedFor.call(c, 2, maxLevel, i)
i = i + 1
}
} else if (level < maxLevel) {
for (c in nn.children) {
nestedFor.call(c, level+1, maxLevel, col)
}
} else {
if (nn.children.count > 0) {
var i = 0
for (c in nn.children) {
span = 1
colSpan.call(c)
var cn = col + 1
if (maxLevel == 1) cn = i + 1
var s = Fmt.swrite("| style=\"background: $s \" colspan=$d | $s", cols[cn], span, c.name)
lines.add(s)
i = i + 1
}
} else {
lines.add(l2)
}
}
}
for (maxLevel in 1...depth) {
nestedFor.call(n, 1, maxLevel, 0)
if (maxLevel < depth-1) lines.add(l1)
}
lines.add("|}")
return lines.join("\n")
}
var outline = """
Display an outline as a nested table.
Parse the outline to a tree,
measuring the indent of each line,
translating the indentation to a nested structure,
and padding the tree to even depth.
count the leaves descending from each node,
defining the width of a leaf as 1,
and the width of a parent node as a sum.
(The sum of the widths of its children)
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.
"""
var yellow = "#ffffe6;"
var orange = "#ffebd2;"
var green = "#f0fff0;"
var blue = "#e6ffff;"
var pink = "#ffeeff;"
var cols = [yellow, orange, green, blue, pink]
var iNodes = makeIndent.call(outline, 4)
var n = NNode.new("", [])
toNest.call(iNodes, 0, 0, n)
System.print(toMarkup.call(n, cols, 4))
System.print("\n")
var outline2 = """
Display an outline as a nested table.
Parse the outline to a tree,
measuring the indent of each line,
translating the indentation to a nested structure,
and padding the tree to even depth.
count the leaves descending from each node,
defining the width of a leaf as 1,
and the width of a parent node as a sum.
(The sum of the widths of its children)
Propagating the sums upward as necessary.
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.
Optionally add color to the nodes.
"""
var cols2 = [blue, yellow, orange, green, pink]
var n2 = NNode.new("", [])
var iNodes2 = makeIndent.call(outline2, 4)
toNest.call(iNodes2, 0, 0, n2)
System.print(toMarkup.call(n2, cols2, 4))</syntaxhighlight>
{{out}}
{| class="wikitable" style="text-align: center;"
|-
| style="background: #ffffe6; " colSpan=7 | Display an outline as a nested table.
|-
| style="background: #ffebd2; " colspan=3 | Parse the outline to a tree,
| style="background: #f0fff0; " colspan=2 | count the leaves descending from each node,
| style="background: #e6ffff; " colspan=2 | and write out a table with 'colspan' values
|-
| style="background: #ffebd2; " colspan=1 | measuring the indent of each line,
| style="background: #ffebd2; " colspan=1 | translating the indentation to a nested structure,
| style="background: #ffebd2; " colspan=1 | and padding the tree to even depth.
| style="background: #f0fff0; " colspan=1 | defining the width of a leaf as 1,
| style="background: #f0fff0; " colspan=1 | and the width of a parent node as a sum.
| style="background: #e6ffff; " colspan=1 | either as a wiki table,
| style="background: #e6ffff; " colspan=1 | or as HTML.
|-
| |
| |
| |
| |
| style="background: #f0fff0; " colspan=1 | (The sum of the widths of its children)
| |
| |
|}
{| class="wikitable" style="text-align: center;"
|-
| style="background: #e6ffff; " colSpan=9 | Display an outline as a nested table.
|-
| style="background: #ffffe6; " colspan=3 | Parse the outline to a tree,
| style="background: #ffebd2; " colspan=3 | count the leaves descending from each node,
| style="background: #f0fff0; " colspan=2 | and write out a table with 'colspan' values
| style="background: #ffeeff; " colspan=1 | Optionally add color to the nodes.
|-
| style="background: #ffffe6; " colspan=1 | measuring the indent of each line,
| style="background: #ffffe6; " colspan=1 | translating the indentation to a nested structure,
| style="background: #ffffe6; " colspan=1 | and padding the tree to even depth.
| style="background: #ffebd2; " colspan=1 | defining the width of a leaf as 1,
| style="background: #ffebd2; " colspan=2 | and the width of a parent node as a sum.
| style="background: #f0fff0; " colspan=1 | either as a wiki table,
| style="background: #f0fff0; " colspan=1 | or as HTML.
| |
|-
| |
| |
| |
| |
| style="background: #ffebd2; " colspan=1 | (The sum of the widths of its children)
| style="background: #ffebd2; " colspan=1 | Propagating the sums upward as necessary.
| |
| |
|}
=={{header|zkl}}==
<
var [const] indent=" "*100; // no tabs
Line 1,518 ⟶ 3,740:
out.writeln("|}");
out.text
}</
<
#<<<
"Display an outline as a nested table.
Line 1,537 ⟶ 3,759:
rows,cols,title,trees := parseOutline(outlineText);
makeMarkup(rows,cols,title,trees).println();</
{{out}}
{| class="wikitable" style="text-align: center;"
Line 1,563 ⟶ 3,785:
And the Raku example:
<
#<<<
"Display an outline as a nested table.
Line 1,583 ⟶ 3,805:
rows,cols,title,trees := parseOutline(outlineText);
makeMarkup(rows,cols,title,trees).println();</
{{out}}
{| class="wikitable" style="text-align: center;"
|