RE: [xsl] Count a substring of an attribute in childnodes

Subject: RE: [xsl] Count a substring of an attribute in childnodes
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Wed, 9 Mar 2005 14:02:58 -0000
> I tried 
> select="sum(number(colspec[contains(@colwidth,'*')]/substring-
> before(@colwidth,'*')))" 
> instead, but this does not work either. 

The number() function returns a single number. Summing over a single number
obviously isn't going to do any good. You're thrashing around rather than
thinking about the problem clearly.

Your problem has the general form:

1) select a set of nodes
2) for each node, compute a number
3) find the sum of the numbers

This is often referred to as the "sum of price times quantity" problem,
though in your case the computation in step 2 is slightly different. Let's
solve the general problem by saying that in step 2, we want to call a
function f(node)->number. Having got a general solution, it's then easy to
apply it to your case and to many other similar cases just by changing the
function f.

In XPath 2.0 the solution can be written

  sum(for $n in $nodes return f($n))

or more concisely

  sum($nodes/f(.))

You've been offered two solutions for XSLT/XPath 1.0, and I'll add a third.

The first solution is recursion. Cutting out the XSLT noise, the solution
can be expressed:

total($nodes):
choose
 when count($nodes)=0
   return 0 
 otherwise
   return f($nodes[1]) + total($nodes[position()>1])

The second solution is Dimitre's functional programming approach. This is
the one that's closest to the way I expressed the problem above. Note that I
described a three-stage approach in which stage 2 involves computing some
function. This is a classic higher-order function. The three-step algorithm
is a function that takes two arguments: the sequence of nodes, and the
function to be computed in step 2. Dimitre's FXSL library allows you to
write the solution in this form. It takes some getting used to, but it's the
most economical way of solving the problem; and although it involves calling
the FXSL library, that's written in pure XSLT 1.0 so it should be completely
portable.

The third solution uses the pipeline design pattern mentioned in my mail
earlier today. Here you do the job in two steps. The output of the first
step is a tree in which the elements carry an attribute that's the computed
number (the value of f(node)). The second step uses the sum() function to
sum over these attributes. 

Michael Kay
http://www.saxonica.com/

Current Thread