From: max vorticity on
I would like to reorder rows in an html table so I thought it might be
good time to learn how to use tdom instead of brute force parsing of
the html with string commands, but now I've spent a bit more time than
I should have to do this. I can get a list of nodes in the order I
want, but I'm not sure how to change the parent's child list to be in
this order. Do I first clone the parent node and add in each child in
order, use insertBefore or replaceChild? For the latter two, I was
unsure if I needed to be concerned with the document fragment list the
documentation mentions. I sure this is fairly straight forward, but
I don't really have much knowledge of working with trees or DOM.

From: Hai Vu on
On Feb 23, 10:42 am, max vorticity <max.vortic...(a)verizon.net> wrote:
> I would like to reorder rows in an html table so I thought it might be
> good time to learn how to use tdom instead of brute force parsing of
> the html with string commands, but now I've spent a bit more time than
> I should have to do this.  I can get a list of nodes in the order I
> want, but I'm not sure how to change the parent's child list  to be in
> this order.  Do I first clone the parent node and add in each child in
> order,  use insertBefore or replaceChild?  For the latter two, I was
> unsure if I needed to be concerned with the document fragment list the
> documentation mentions.   I sure this is fairly straight forward, but
> I don't really have much knowledge of working with trees or DOM.

Check out removeChild insertChild, and appendChild. You can use these
to reorder the child nocdes. Here is a short sample:

-----

package require tdom

set xmltext {
<root>
<node1>this is node1</node1>
<node2>this is node2</node2>
</root>
}

set doc [dom parse $xmltext]; # Turn XML text into dom
set root [$doc documentElement]; # The root node

puts "Before:"
puts [$root asXML]

# Swap node1 and node2
set node1 [$root firstChild]; # node1
$root removeChild $node1; # Remove node1 from root
$root appendChild $node1; # Put node1 after node2

puts "After:"
puts [$root asXML]
From: MartinLemburg on
Hi,

since there seems to be no way to sort the DOM tree in tdom, what's
about this:

proc sortCommand {nodeValueCommand options node1 node2} {
# get the values to compare
#
set value1 [{*}[string map [list %N $node1]
$nodeValueCommand]];
set value2 [{*}[string map [list %N $node2]
$nodeValueCommand]];

# care for the nocase option
#
if {"-nocase" in $options} {
set value1 [string tolower $value1];
set value2 [string tolower $value2];
}

# compare in dictionary style
#
if {"-dictionary" in $options} {
if {$value1 eq $value2} {
return 0;
}

set list [lsort -dictionary [list $value1 $value2]];

if {$value1 eq [lindex $list 0]} {
return -1;
}

return 1;
}

# compare non-numerical values
#
if { ("-real" ni $options && "-integer" ni $options)
|| ![string is double -strict $value1]
|| ![string is double -strict $value2]} {
return [string compare $value1 $value2];
}

# compare numerical values
#
if {$value1 == $value2} {
return 0;
} elseif {$value1 > $value2} {
return 1;
}

return -1;
}

proc sortNodesByCommand {parentNode nodeValueCommand args} {
# get the child nodes and sort them
#
set childNodes [$parentNode childNodes];
set sortedNodes [lsort \
{*}$args \
-command [list sortCommand $nodeValueCommand $args] \
$childNodes \
];

if {$sortedNodes eq $childNodes} {
return;
}

# remove all child nodes from the parent node
#
foreach childNode $childNodes {
$parentNode removeChild $childNode;
}

# add the child nodes in the new order
#
foreach childNode $sortedNodes {
$parentNode appendChild $childNode;
}

return;
}

proc sortNodesByName {parentNode args} {
sortNodesByCommand $parentNode {%N nodeName} {*}$args -
dictionary;
}

proc sortNodesByValue {parentNode args} {
sortNodesByCommand $parentNode {%N nodeValue} {*}$args;
}

proc sortNodesByText {parentNode args} {
sortNodesByCommand $parentNode {%N text} {*}$args -dictionary;
}

proc sortNodesByAttribute {parentNode attribute args} {
sortNodesByCommand $parentNode {%N getAttribute $attribute} {*}
$args;
}

# examples
#
package require tdom;

set dom [dom parse -html [tDOM::xmlReadFile $htmlFile]];
set doc [$dom documentElement];
set head [$doc firstChild];

sortNodesByName $head;
sortNodesByCommand $body {llength [%N childNodes]};

I've put this to the wiki, too: http://wiki.tcl.tk/25772

Best regards,

Martin

On 23 Feb., 19:42, max vorticity <max.vortic...(a)verizon.net> wrote:
> I would like to reorder rows in an html table so I thought it might be
> good time to learn how to use tdom instead of brute force parsing of
> the html with string commands, but now I've spent a bit more time than
> I should have to do this.  I can get a list of nodes in the order I
> want, but I'm not sure how to change the parent's child list  to be in
> this order.  Do I first clone the parent node and add in each child in
> order,  use insertBefore or replaceChild?  For the latter two, I was
> unsure if I needed to be concerned with the document fragment list the
> documentation mentions.   I sure this is fairly straight forward, but
> I don't really have much knowledge of working with trees or DOM.

From: max vorticity on
On Feb 24, 4:34 am, "MartinLemburg(a)Siemens-PLM"
<martin.lemburg.siemens-...(a)gmx.net> wrote:
> Hi,
>
> since there seems to be no way to sort the DOM tree in tdom, what's
> about this:
>
>     proc sortCommand {nodeValueCommand options node1 node2} {
>         # get the values to compare
>         #
>         set value1 [{*}[string map [list %N $node1]
> $nodeValueCommand]];
>         set value2 [{*}[string map [list %N $node2]
> $nodeValueCommand]];
>
>         # care for the nocase option
>         #
>         if {"-nocase" in $options} {
>             set value1 [string tolower $value1];
>             set value2 [string tolower $value2];
>         }
>
>         # compare in dictionary style
>         #
>         if {"-dictionary" in $options} {
>             if {$value1 eq $value2} {
>                 return 0;
>             }
>
>             set list [lsort -dictionary [list $value1 $value2]];
>
>             if {$value1 eq [lindex $list 0]} {
>                 return -1;
>             }
>
>             return 1;
>         }
>
>         # compare non-numerical values
>         #
>         if {   ("-real" ni $options && "-integer" ni $options)
>             || ![string is double -strict $value1]
>             || ![string is double -strict $value2]} {
>             return [string compare $value1 $value2];
>         }
>
>         # compare numerical values
>         #
>         if {$value1 == $value2} {
>             return 0;
>         } elseif {$value1 > $value2} {
>             return 1;
>         }
>
>         return -1;
>     }
>
>     proc sortNodesByCommand {parentNode nodeValueCommand args} {
>         # get the child nodes and sort them
>         #
>         set childNodes  [$parentNode childNodes];
>         set sortedNodes [lsort \
>             {*}$args \
>             -command [list sortCommand $nodeValueCommand $args] \
>             $childNodes \
>         ];
>
>         if {$sortedNodes eq $childNodes} {
>             return;
>         }
>
>         # remove all child nodes from the parent node
>         #
>         foreach childNode $childNodes {
>             $parentNode removeChild $childNode;
>         }
>
>         # add the child nodes in the new order
>         #
>         foreach childNode $sortedNodes {
>             $parentNode appendChild $childNode;
>         }
>
>         return;
>     }
>
>     proc sortNodesByName {parentNode args} {
>         sortNodesByCommand $parentNode {%N nodeName} {*}$args -
> dictionary;
>     }
>
>     proc sortNodesByValue {parentNode args} {
>         sortNodesByCommand $parentNode {%N nodeValue} {*}$args;
>     }
>
>     proc sortNodesByText {parentNode args} {
>         sortNodesByCommand $parentNode {%N text} {*}$args -dictionary;
>     }
>
>     proc sortNodesByAttribute {parentNode attribute args} {
>         sortNodesByCommand $parentNode {%N getAttribute $attribute} {*}
> $args;
>     }
>
>     # examples
>     #
>     package require tdom;
>
>     set dom [dom parse -html [tDOM::xmlReadFile $htmlFile]];
>     set doc     [$dom documentElement];
>     set head    [$doc firstChild];
>
>     sortNodesByName $head;
>     sortNodesByCommand $body {llength [%N childNodes]};
>
> I've put this to the wiki, too:http://wiki.tcl.tk/25772
>
> Best regards,
>
> Martin
>
> On 23 Feb., 19:42, max vorticity <max.vortic...(a)verizon.net> wrote:
>
> > I would like to reorder rows in an html table so I thought it might be
> > good time to learn how to use tdom instead of brute force parsing of
> > the html with string commands, but now I've spent a bit more time than
> > I should have to do this.  I can get a list of nodes in the order I
> > want, but I'm not sure how to change the parent's child list  to be in
> > this order.  Do I first clone the parent node and add in each child in
> > order,  use insertBefore or replaceChild?  For the latter two, I was
> > unsure if I needed to be concerned with the document fragment list the
> > documentation mentions.   I sure this is fairly straight forward, but
> > I don't really have much knowledge of working with trees or DOM.
>
>

I found that just reusing the node with appendChild reordered the
nodes. So if $nodes is a list of the nodes in the desired order:

foreach n $nodes {$parent appendChild $n}

The above worked in my case but I don't know if this is really a safe
way to reoder the nodes.
From: Rolf Ade on
max vorticity <max.vorticity(a)verizon.net> wrote:
>> On 23 Feb., 19:42, max vorticity <max.vortic...(a)verizon.net> wrote:
>>
>> > I would like to reorder rows in an html table so I thought it might be
>> > good time to learn how to use tdom instead of brute force parsing of
>> > the html with string commands, but now I've spent a bit more time than
>> > I should have to do this. �I can get a list of nodes in the order I
>> > want, but I'm not sure how to change the parent's child list �to be in
>> > this order. �Do I first clone the parent node and add in each child in
>> > order, �use insertBefore or replaceChild? �For the latter two, I was
>> > unsure if I needed to be concerned with the document fragment list the
>> > documentation mentions. � I sure this is fairly straight forward, but
>> > I don't really have much knowledge of working with trees or DOM.
>>
>>
>
>I found that just reusing the node with appendChild reordered the
>nodes. So if $nodes is a list of the nodes in the desired order:
>
>foreach n $nodes {$parent appendChild $n}
>
>The above worked in my case but I don't know if this is really a safe
>way to reoder the nodes.

That should be save and work. If not, report as bug. The appendChild
subcommand unlinks the node to append from its current place in the
tree and appends it as last child of $parent. So, a receipt to reorder
childs is: get the childs with [$parent childNodes], reorder the
returned list of nodes as you need and bring the childs in the tree
into the new order as shown above.

A note for the second step: Using lsort -command can be slow, which is
well known. Depending on what you want to sort the childs, a usual
advice is: build up a sort list like

set sortlist [list]
foreach node $childlist {
lappend sortlist [list [$node text] $node]
}
set sortedlist [lsort -index 0 <maybe more options> $sortlist]
set sortedchilds [list]
foreach lelm $sortedlist {
lappend sortedchilds [lindex $lelm 1]
}

rolf