473,237 Members | 1,162 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,237 software developers and data experts.

Tip: Finding & counting unique nodes in XSL

When looking for a method to fetch unique elements and counting the
number of occurences of each of them, I found quite a lot of gross
examples of complex XSL. But after realizing the subtle difference
between "." and "current()", I found a neat way of doing the same
without keys or generate-id():

<xsl:template match="/">
<!-- Selects all "new" elements -->
<xsl:for-each select="//Name[not(.=preceding::Name)]">
<!-- Display the element -->
<xsl:value-of select="."/>
<!-- Count the number of occurences of the element -->
<xsl:value-of select="count(//Name[.=current()])"/>
</xsl:for-each>
</xsl:template>

The clue to why the last "value-of" works is that "." refers dynamically
to each of the "Name" elements in the file, while "current()" refers to
the "Name" element in the sorrounding "for-each" element.

If YOU have found a "neat" way of doing something with XSL, it would be
great if you could post it here as well, preferably as a separate thread
with something like "Tip:" in the beginning of the subject.

--
Victor
Jul 20 '05 #1
4 6541
Victor Engmark <vi************@cern.ch> wrote in news:c6b8bv$suu$1
@sunnews.cern.ch:
When looking for a method to fetch unique elements and counting the
number of occurences of each of them, I found quite a lot of gross
examples of complex XSL. But after realizing the subtle difference
between "." and "current()", I found a neat way of doing the same
without keys or generate-id():

<xsl:template match="/">
<!-- Selects all "new" elements -->
<xsl:for-each select="//Name[not(.=preceding::Name)]">
<!-- Display the element -->
<xsl:value-of select="."/>
<!-- Count the number of occurences of the element -->
<xsl:value-of select="count(//Name[.=current()])"/>
</xsl:for-each>
</xsl:template>

The clue to why the last "value-of" works is that "." refers dynamically
to each of the "Name" elements in the file, while "current()" refers to
the "Name" element in the sorrounding "for-each" element.

If YOU have found a "neat" way of doing something with XSL, it would be
great if you could post it here as well, preferably as a separate thread
with something like "Tip:" in the beginning of the subject.


Thanks Victor, a very neat solution. Easier to apply than the keys and
generate-id() approach.
Jul 20 '05 #2
Piet Blok <p@b> wrote in news:40**********************@news.wanadoo.nl:
Victor Engmark <vi************@cern.ch> wrote in news:c6b8bv$suu$1
@sunnews.cern.ch:
When looking for a method to fetch unique elements and counting the
number of occurences of each of them, I found quite a lot of gross
examples of complex XSL. But after realizing the subtle difference
between "." and "current()", I found a neat way of doing the same
without keys or generate-id():

<xsl:template match="/">
<!-- Selects all "new" elements -->
<xsl:for-each select="//Name[not(.=preceding::Name)]">
<!-- Display the element -->
<xsl:value-of select="."/>
<!-- Count the number of occurences of the element -->
<xsl:value-of select="count(//Name[.=current()])"/>
</xsl:for-each>
</xsl:template>

The clue to why the last "value-of" works is that "." refers
dynamically to each of the "Name" elements in the file, while
"current()" refers to the "Name" element in the sorrounding
"for-each" element.

If YOU have found a "neat" way of doing something with XSL, it would
be great if you could post it here as well, preferably as a separate
thread with something like "Tip:" in the beginning of the subject.


Thanks Victor, a very neat solution. Easier to apply than the keys and
generate-id() approach.


Victor, I tried to enhance one of my XSL stylesheets that I made awhile
ago to create a birthday calendar. To my own surprise I found that I
already abandoned the use of keys and generate-id() (however not so nice
and clean as you did). My problem was that I had to check on only a part
of some value (the month number that was embedded between year and day
in the same tag). Therefore I had to do some string manipulation.

I changed that implementation today and now I use the preceding:: axes.
So it looks already better now.

However, I just cannot get rid of an ugly xsl:if. In my solution I
select all elements and then I decide if the current element is a "new"
element. See below:
<xsl:template match="/">
<!-- select all Name elements -->
<xsl:for-each select="//Name">
<!-- test if this Name element is new, based on the first character -->
<xsl:if test="not(substring(current(),1,1) = substring
(preceding::Name[substring(.,1,1)=substring(current(),1,1)],1,1))">
<!-- Display the first character -->
<xsl:value-of select="substring(.,1,1)"/>
<!-- Count occurences of element with the same starting character -->
<xsl:value-of select="count(//Name[substring(.,1,1)
=substring(current(),1,1)])"/>
</xsl:if>
</xsl:for-each>
</xsl:template>

Can you think of any way to move the if structure into the select
clause?

In a way the problem seems to be that there are three contexts to deal
with: the context node (in this case the root node), the node that is
currently under investigation for selection and the node that is
compared to (between the square brackets).

Any thoughts would be welcome.

Piet
Jul 20 '05 #3
Piet Blok wrote:
However, I just cannot get rid of an ugly xsl:if. In my solution I
select all elements and then I decide if the current element is a "new"
element. See below:
<xsl:template match="/">
<!-- select all Name elements -->
<xsl:for-each select="//Name">
<!-- test if this Name element is new, based on the first character -->
<xsl:if test="not(substring(current(),1,1) = substring
(preceding::Name[substring(.,1,1)=substring(current(),1,1)],1,1))">
<!-- Display the first character -->
<xsl:value-of select="substring(.,1,1)"/>
<!-- Count occurences of element with the same starting character -->
<xsl:value-of select="count(//Name[substring(.,1,1)
=substring(current(),1,1)])"/>
</xsl:if>
</xsl:for-each>
</xsl:template>

Can you think of any way to move the if structure into the select
clause?

In a way the problem seems to be that there are three contexts to deal
with: the context node (in this case the root node), the node that is
currently under investigation for selection and the node that is
compared to (between the square brackets).


First, if you can split the Name into several strings, that would be a
good start. If you have control over the structure of the XML file, keep
each piece of information separate. Otherwise you'll get into the same
mess I was in when trying to make sense of MS Project XML files (gross!).

Then there is a funny thing about the test (changed the substring
function to a() to make it obvious):
a(current) = a(preceding::Name[a(.)=a(current)])
So first you find a preceding Name which equals the current one, and
then compare it to the current one? This sounds like double work.
a(current) = a(preceding::Name) should be enough.

By changing according to the previous paragraphs, you would have
something like a Name element and a New element, and you can do the
for-each as follows (providing that the New element occurs after the Name):
<xsl:for-each select="//Name[not(./following-sibling::New =
preceding::Name/following-sibling::New)]">

I haven't tested this, but perhaps someone with more XSL experience can
point out any obvious errors.

--
Victor
Jul 20 '05 #4
Victor <en************@orakel.ntnu.no> wrote in
news:c9**********@sunnews.cern.ch:
Piet Blok wrote:
However, I just cannot get rid of an ugly xsl:if. In my solution I
select all elements and then I decide if the current element is a
"new" element. See below:
<xsl:template match="/">
<!-- select all Name elements -->
<xsl:for-each select="//Name">
<!-- test if this Name element is new, based on the first character
-->
<xsl:if test="not(substring(current(),1,1) =
substring
(preceding::Name[substring(.,1,1)=substring(current(),1,1)],1,1))">
<!-- Display the first character -->
<xsl:value-of select="substring(.,1,1)"/>
<!-- Count occurences of element with the same starting character -->
<xsl:value-of
select="count(//Name[substring(.,1,1)
=substring(current(),1,1)])"/>
</xsl:if>
</xsl:for-each>
</xsl:template>

Can you think of any way to move the if structure into the select
clause?

In a way the problem seems to be that there are three contexts to
deal with: the context node (in this case the root node), the node
that is currently under investigation for selection and the node that
is compared to (between the square brackets).


First, if you can split the Name into several strings, that would be a
good start. If you have control over the structure of the XML file,
keep each piece of information separate. Otherwise you'll get into the
same mess I was in when trying to make sense of MS Project XML files
(gross!).

Then there is a funny thing about the test (changed the substring
function to a() to make it obvious):
a(current) = a(preceding::Name[a(.)=a(current)])
So first you find a preceding Name which equals the current one, and
then compare it to the current one? This sounds like double work.
a(current) = a(preceding::Name) should be enough.

By changing according to the previous paragraphs, you would have
something like a Name element and a New element, and you can do the
for-each as follows (providing that the New element occurs after the
Name): <xsl:for-each select="//Name[not(./following-sibling::New =
preceding::Name/following-sibling::New)]">

I haven't tested this, but perhaps someone with more XSL experience
can point out any obvious errors.


Thanks Victor,

To answer your first advise: yes I have full control over the XML files,
so I could decide to split up all information into separate elements
(most of my XML work is just experimental). However, there are reasons
why I sometimes delibarately choose not to do so. In the case I
presented as an example, I wanted to do something with the first
character of some element, typically to create an index or something
alike. In another case I tried to deal with a date, in order to create a
birthday calendar. In again another case I wanted to do something with a
weeknumber derived from a date from some XML element. What I want to
achieve is to construct XML files as simple as possible and then, when
need arise, create XSL transformation sheets for specific purposes.
Actually, when I am designing some XML format, I dont want to take into
account all possible uses I might make of it. It is exactly this what
makes XML so attractive to me. Naturally, since my XML formats are not
optimized on any specific use, my XSL stylesheets may from time to time
be somewhat complex.

The second part of your comment is something that needs more thinking,
so I cannot answer you instantly. I will study it and reply when I have
done so (these are things that I have to do in my spare time, mostly
weekends).

I greatly appreciate the neat solution you found for a common problem.
When I ever find something that may be as usefull as your TIP I
certainly will post it the way you did.

Piet

Jul 20 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

9
by: Rolf Kemper | last post by:
Dear Experts, I got stuck with the following problem and need your help. What I wnat to do is to get a set of distinct nodes. Before the distinct I have selected the multiple occourences...
4
by: webdev | last post by:
lo all, some of the questions i'll ask below have most certainly been discussed already, i just hope someone's kind enough to answer them again to help me out.. so i started a python 2.3...
8
by: Paul J Lay | last post by:
I am trying to find nodes in an xml document using the XmlDocument class and having some problems. Everything seems to work OK when there are no encoded characters (< = &lt;) but when I try to...
9
by: dave m | last post by:
I need to be able to retrieve a unique ID from a users PC. I needs to be something a user could not easily change, like the computer name. Could someone point me in the right direction to find ...
1
by: Daniel Hilgarth | last post by:
Hello, I am currently trying to use XSLT for the creation of multiple HTML-files from a single XML-File. This HTML-files need to have links to each other. The following information might be...
2
by: akshaycjoshi | last post by:
I have got one treeview contol in which i create nodes dynamically. I want to set a tool tip for each of the node created. I went throught this tutorial How to add a ToolTip to a TreeNode in...
3
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 3 Jan 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). For other local times, please check World Time Buddy In...
0
by: jianzs | last post by:
Introduction Cloud-native applications are conventionally identified as those designed and nurtured on cloud infrastructure. Such applications, rooted in cloud technologies, skillfully benefit from...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 7 Feb 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:30 (7.30PM). In this month's session, the creator of the excellent VBE...
0
by: fareedcanada | last post by:
Hello I am trying to split number on their count. suppose i have 121314151617 (12cnt) then number should be split like 12,13,14,15,16,17 and if 11314151617 (11cnt) then should be split like...
0
by: stefan129 | last post by:
Hey forum members, I'm exploring options for SSL certificates for multiple domains. Has anyone had experience with multi-domain SSL certificates? Any recommendations on reliable providers or specific...
0
Git
by: egorbl4 | last post by:
Скачал я git, хотел начать настройку, а там вылезло вот это Что это? Что мне с этим делать? ...
0
by: MeoLessi9 | last post by:
I have VirtualBox installed on Windows 11 and now I would like to install Kali on a virtual machine. However, on the official website, I see two options: "Installer images" and "Virtual machines"....
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.