[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: One nodeset, multiple branches

Subject: Re: One nodeset, multiple branches
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Wed, 19 Dec 2001 17:44:43 +0000
multiple bra
Hi Eric,

> maybe Jeni will pop in and explain the deduplication process!

You rubbed my lamp...

Actually, this is another example where deduplication is not
particularly straight forward (the other one being deduplication
across multiple documents earlier today) :(

You only want to collect the right elements whose combination of
module and access attributes aren't the same as other right elements
that you're collecting.

You can put together a node set of the right elements for the user and
their groups as Wendell has shown you, so something like:

  <xsl:variable name="groups"
                select="/access/groups/group" />
  <xsl:variable name="user"
                select="/access/users/user[username = 'evitiello']" />
  <xsl:variable name="user-groups"
                select="$user/userGroups/group" />
  <xsl:variable name="rights"
    select="$user/rights/right |
            $groups/group[@name = $user-groups/@name]/rights/right" />

One method of doing getting the unique rights is to filter the right
elements you collect to only include the *first* right element that
you're interested in with a particular module+access combination.

Set up a key (with a top-level xsl:key element) that indexes all the
right elements according to their module+access combination. You can
create a 'value' for each right element, based on their module and
access, by concatenating the values of the two attributes (with a
separator just in case you have a module like 'newse' and a access
like 'dit', which could be confused with the module 'news' and access
'edit'):

<xsl:key name="rights" match="right"
         use="concat(@module, '+', @access)" />

You can call the key with the key() function - first argument is the
name of the key, second argument is the module+access combination. For
example:

  key('rights', 'news+add')

will retrieve *all* the right elements in the document with a module
attribute equal to 'news' and an access attribute equal to 'add'.
You're actually only interested in the ones that you've collected
together in the $rights variable, so you need to filter this set to
only include those that are in the $rights node set. You can do this
with set logic, as follows:

  key('rights', 'news+add')[count(.|$rights) = count($rights)]

Then you can find the first of these in document order with a
positional predicate at the end:

  key('rights', 'news+add')[count(.|$rights) = count($rights)][1]

That gives you the first 'right' element in the user's rights whose
module is 'news' and whose access is 'add'.

If you're looking at a right element whose module is 'news' and whose
access is 'add', you can work out whether that right element is the
same as the one you've just retrieved using set logic again:

  count(.|key('rights', 'news+add')[count(.|$rights) =
                                    count($rights)][1]) = 1

or:

  generate-id() =
  generate-id(key('rights', 'news+add')[count(.|$rights) =
                                        count($rights)])

The value that you use as the second argument to the key() function
could be based on the right element that you're looking at, so for any
right element, you could work out whether it was the first in the
document with that module and access using:

  count(.|key('rights', concat(@module, '+', @access))
            [count(.|$rights) = count($rights)][1]) = 1

So you could find all the unique right elements in the list using:

  <xsl:variable name="unique-rights"
    select="$rights
              [count(.|key('rights', concat(@module, '+', @access))
                         [count(.|$rights) = count($rights)][1]) = 1]" />

Since count($rights) is going to be the same each time, it might be
slightly more efficient to assign that to a variable at the start:

  <xsl:variable name="nrights" select="count($rights)" />
  <xsl:variable name="unique-rights"
    select="$rights
              [count(.|key('rights', concat(@module, '+', @access))
                         [count(.|$rights) = $nrights][1]) = 1]" />

If you find that XPath monstrous, you're not alone! :)

So alternatives are:

 - adapt the distinct template that I posted earlier today (polish my
   lamp again if you need help with that)
 - use saxon:distinct() if you're using Saxon (wipe Mike's lamp for
   that one)
 - use Dimitre's foldl generic template (buff up Dimitre's lamp for
   that)

Cheers,

Jeni (in panto mood)

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.