XSL to Select Unique and Sort

Hello Experts,

I am trying to create a XSL to give the output below:


Time            Judge      Case            Defendant

09:00 AM  EAM       CR04003107    BLOUGH, JOE
10:00 AM  EAM       CR04004200      BLOUGH, FRED



I need to select only Court=CR3 and unique cases base on the first ten characters of CaseNo.

Any suggestions on how to approach this would be appreciated.
 
XML DATA:

<?xml version="1.0" encoding="ISO-8859-1"?>
<CRDocket>
<Case>
<Time>09:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR3</Court>
<CaseNo>CR04003107-01</CaseNo>
<Defendant>BLOUGH, JOE</Defendant>
</Case>
<Case>
<Time>09:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR3</Court>
<CaseNo>CR04003107-02</CaseNo>
<Defendant>BLOUGH, JOE</Defendant>
</Case>
<Case>
<Time>09:00 AM</Time>
<Judge>NAT</Judge>
<Court>CR5</Court>
<CaseNo>CR11000475-01</CaseNo>
<Defendant>DOUGH, JANE</Defendant>
</Case>
<Case>
<Time>10:00 AM</Time>
<Judge>EAM</Judge>
<Court>CR3</Court>
<CaseNo>CR04004200-02</CaseNo>
<Defendant>BLOUGH, FRED</Defendant>
</Case>
</CRDocket>

Open in new window

GJDuquetteAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Geert BormansInformation ArchitectCommented:
This will do that
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:key name="case" match="Case" use="substring(CaseNo, 1, 10)"/>
    <xsl:strip-space elements="*"/>
    <xsl:output method="html"/>
    <xsl:template match="CRDocket">
        <table>
            <tr>
                <th>Time</th>
                <th>Judege</th>
                <th>Case</th>
                <th>Defendant</th>
            </tr>
            <xsl:apply-templates select="Case[generate-id() = generate-id(key('case', substring(CaseNo, 1, 10))[1])]"/>
        </table>
    </xsl:template>
    <xsl:template match="Case">
        <tr>
            <td><xsl:value-of select="Time"/></td>
            <td><xsl:value-of select="Judge"/></td>
            <td><xsl:value-of select="substring(CaseNo, 1, 10)"/></td>
            <td><xsl:value-of select="Defendant"/></td>
        </tr>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0
sweetfa2Commented:
<xsl:stylesheet version="1.0"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                exclude-result-prefixes="xsi xsl xsd">

   <xsl:output method="text" version="1.0" encoding="UTF-8" indent="no" omit-xml-declaration="yes"/>
  
  <xsl:key name="CaseNoKey" match="Case" use="substring(CaseNo,1,10)"/>
  
  <xsl:template match="/CRDocket/Case[Court!='CR3']"/>

  <xsl:template match="/CRDocket">
  	<xsl:text>Time       Judge   Case          Defendant</xsl:text>
  	<xsl:for-each select="Case[generate-id() = generate-id(key('CaseNoKey', substring(CaseNo, 1, 10))[1])][Court='CR3']">
	  	<xsl:text disable-output-escaping="yes">
</xsl:text>
	  	<xsl:value-of select="Time"/>
	  	<xsl:text disable-output-escaping="yes">    </xsl:text>
	  	<xsl:value-of select="Judge"/>
	  	<xsl:text disable-output-escaping="yes">    </xsl:text>
	  	<xsl:value-of select="substring(CaseNo,1,10)"/>
	  	<xsl:text disable-output-escaping="yes">    </xsl:text>
	  	<xsl:value-of select="Defendant"/>
  	</xsl:for-each>
  </xsl:template>
	-->
	
</xsl:stylesheet>

Open in new window

0
Geert BormansInformation ArchitectCommented:
for uniqyueness, I used the muenchian technique

http://www.jenitennison.com/xslt/grouping/muenchian.xml
0
Cloud Class® Course: CompTIA Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

Geert BormansInformation ArchitectCommented:
I see that I missed one reuirement, the one about the Court = CR3

change this line
    <xsl:key name="case" match="Case" use="substring(CaseNo, 1, 10)"/>

into this
    <xsl:key name="case" match="Case[Court = 'CR3']" use="substring(CaseNo, 1, 10)"/>

Full code repeated below

It spits out a HTML table,
but it can easily be changed into text only

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:key name="case" match="Case[Court = 'CR3']" use="substring(CaseNo, 1, 10)"/>
    <xsl:strip-space elements="*"/>
    <xsl:output method="html"/>
    <xsl:template match="CRDocket">
        <table>
            <tr>
                <th>Time</th>
                <th>Judege</th>
                <th>Case</th>
                <th>Defendant</th>
            </tr>
            <xsl:apply-templates select="Case[generate-id() = generate-id(key('case', substring(CaseNo, 1, 10))[1])]"/>
        </table>
    </xsl:template>
    <xsl:template match="Case">
        <tr>
            <td><xsl:value-of select="Time"/></td>
            <td><xsl:value-of select="Judge"/></td>
            <td><xsl:value-of select="substring(CaseNo, 1, 10)"/></td>
            <td><xsl:value-of select="Defendant"/></td>
        </tr>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
sweetfa2Commented:
Whilst Gertone's revised answer is now correct I personally prefer to see all discriminators in the same line for ease of maintenance.  You could change Gertones first answer to

<xsl:apply-templates select="Case[generate-id() = generate-id(key('case', substring(CaseNo, 1, 10))[1])][Court='CR3']"/>

Open in new window


to achieve the same result whilst keeping the discrimantors together
0
GJDuquetteAuthor Commented:
Very nice! How can we add sorting by time and defendant?
0
sweetfa2Commented:
  	<xsl:for-each select="Case[generate-id() = generate-id(key('CaseNoKey', substring(CaseNo, 1, 10))[1])][Court='CR3']">
  		<xsl:sort select="Time,Defendant" order="ascending"/>

Open in new window

0
Geert BormansInformation ArchitectCommented:
@sweetfa2,

I appreciate that you have a different style and share your personal preference,
but your alternative is fundamentally wrong.

First let me say that in my point of view you have two types of discriminators:
- the technical ones (from muenchian) are only there to make sure you select unique nodes
- the business ones (value of Court)
Seperating both technical discriminators from business discriminators in my opninion gives better maintenance
But I accept this is a matter of taste and you posed it as a personal reference, so no problem with that

Now, you are selecting the first unique node matching the 'case' key and after that you add the court restriction.
That works in this example. But you seem to pose it as a general approach.
What would happen if a case changes court after the first hearing
I have altered the XML for this thought experiment

    <Case>
        <Time>09:00 AM</Time>
        <Judge>EAM</Judge>
        <Court>CR4</Court>
        <CaseNo>CR04003107-01</CaseNo>
        <Defendant>BLOUGH, JOE</Defendant>
    </Case>
    <Case>
        <Time>09:00 AM</Time>
        <Judge>EAM</Judge>
        <Court>CR3</Court>
        <CaseNo>CR04003107-02</CaseNo>
        <Defendant>BLOUGH, JOE</Defendant>
    </Case>

You would simply miss this case given your approach.
So there is a fundamental error in there
If you would alter my code to fit your prefered style,
you should do this
            <xsl:apply-templates select="Case[generate-id() = generate-id(key('case', substring(CaseNo, 1, 10))[Court = 'CR3'][1])]"/>



0
Geert BormansInformation ArchitectCommented:
Then, sweetfa2s suggestion for sort is wrong

<xsl:sort select="Time,Defendant" order="ascending"/>
is an illegal XPath expression

Here is what you should do
               <xsl:sort select ="Time" order="ascending"/>
                <xsl:sort select="Defendant" order="ascending"/>
will first order on Time (ascending, text data type)
and then on defendant (ascending text data type)

If you want to sort on Defendant first, you can simply swap
                 <xsl:sort select="Defendant" order="ascending"/>
              <xsl:sort select ="Time" order="ascending"/>

This sorting can be used with the for-each approach sweetfa uses,
but it can also be used with my apply-templates approach
(for maintenance reasons I do prefer apply-templates over for-each)

            <xsl:apply-templates select="Case[generate-id() = generate-id(key('case', substring(CaseNo, 1, 10))[1])]">
                <xsl:sort select="Time" order="ascending"/>
                <xsl:sort select="Defendant" order="ascending"/>
            </xsl:apply-templates>
0
Geert BormansInformation ArchitectCommented:
As a final comment

In most XSLT1 processors (there is not much internal optimisation going on)
indexing with a predicate in the key has better performance than being selective later
So, on large datasets there could be a performance benefit in my approach

I also see a lot of d-o-e (disable-output-escape) in sweetfa's code.
It serves nothing and it is generally understood that d-o-e should preferably not be used
              <xsl:text disable-output-escaping="yes">    </xsl:text>
in the spaces there is nothing to escape, so why add it there?
d-o-e is not a mandatory aspect of the XSLT1 recommendation, which means that a processor can still be compliant without supporting it.
The Firefox transformer for example does not support it, and still is considered a compliant XSLT processor.
So use it sparsely and only if you control the processor (don't use it in browser XSLT)

I see a lot of XSLT programmers use d-o-e by default on every xsl:text (if they do that they should also use it on every xsl:value-of to be consistent)
The main reason is that some programs override the XSLTs serialisation settings
but this should be changed in teh java/VB/C# code that is calling the XSLT, not by throwing d-o-e allover the stylesheet
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
XML

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.