tags:

views:

39

answers:

4

This question is related to this post http://stackoverflow.com/questions/3461728/find-maximum-value-of-all-child-elements-and-get-its-parent-element-in-xslt. I asked my question incorrectly. Actually I need output little different. I need to match site node id with worksite node and find out the person who worked more hours for that site.

<root>
    <Site id="S1">
        <othernodes></othernodes>
    </Site>
    <Site id="S2">
        <othernodes></othernodes>
    </Site>
    <Site id="S3">
        <othernodes></othernodes>
    </Site> 
    <WorkSite Person="P1" Site="S1">
        <Hours>8</Hours>
    </WorkSite>
    <WorkSite Person="P1" Site="S2">
        <Hours>2</Hours>
    </WorkSite>
    <WorkSite Person="P1" Site="S3">
        <Hours>20</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S1">
        <Hours>6</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S2">
        <Hours>10</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S3">
        <Hours>21</Hours>
    </WorkSite>
</root>

The transformed content should be like this

<root>
    <site id="S1">
            <othernodes></othernodes>
            <person>P1</person>
    </site>
    <site id="S2">
            <othernodes></othernodes>
            <person>P2</person>
    </site>
    <site id="S3">
            <othernodes></othernodes>
            <person>P1</person>
    </site>
</root>

Can someone help on this?

+1  A: 

Something like this?

<xsl:template match="/">
  <root>
    <xsl:apply-templates select="root/Site" />
  </root>
</xsl:template>

<xsl:template match="/root/Site">
  <xsl:variable name="sid" select="./@id" />
  <site id="{./@id}">
    <othernodes></othernodes>
    <xsl:apply-templates select="/root/WorkSite[@Site = $sid]">
      <xsl:sort select="./Hours" data-type="number" order="descending"/>
    </xsl:apply-templates>
  </site>
</xsl:template>

<xsl:template match="/root/WorkSite">
  <xsl:if test="position() = 1">
    <person><xsl:value-of select="./@Person" /></person>
  </xsl:if>
</xsl:template>
A: 

This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:key name="BySite" match="WorkSite" use="@Site"/>
    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Site/*[last()]">
        <xsl:call-template name="identity"/>
        <person>
            <xsl:value-of select="substring-after(
                                    key('BySite',../@id)
                                      [not(key('BySite',@Site)/Hours
                                           > Hours)]
                                      /@Person,
                                    'P')" />
        </person>
    </xsl:template>
    <xsl:template match="WorkSite"/>
</xsl:stylesheet>

Output:

<root>
    <Site id="S1">
        <othernodes></othernodes>
        <person>1</person>
    </Site>
    <Site id="S2">
        <othernodes></othernodes>
        <person>2</person>
    </Site>
    <Site id="S3">
        <othernodes></othernodes>
        <person>2</person>
    </Site>
</root>
Alejandro
A: 

This is what I would do. (I added the for-each just in case you had multiple people at a site with the exact same hours.)

EDIT: I didn't see the XSLT 1.0 requirement in the other post until after I added my answer. Sorry about that!

XML input:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <Site id="S1">
    <othernodes/>
  </Site>
  <Site id="S2">
    <othernodes/>
  </Site>
  <Site id="S3">
    <othernodes/>
  </Site>
  <WorkSite Person="P1" Site="S1">
    <Hours>8</Hours>
  </WorkSite>
  <WorkSite Person="P1" Site="S2">
    <Hours>2</Hours>
  </WorkSite>
  <WorkSite Person="P1" Site="S3">
    <Hours>20</Hours>
  </WorkSite>
  <WorkSite Person="P2" Site="S1">
    <Hours>6</Hours>
  </WorkSite>
  <WorkSite Person="P2" Site="S2">
    <Hours>10</Hours>
  </WorkSite>
  <WorkSite Person="P2" Site="S3">
    <Hours>21</Hours>
  </WorkSite>
</root>

XSLT 2.0 Stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Site">
    <xsl:variable name="vSiteId">
      <xsl:value-of select="@id"/>
    </xsl:variable>
    <xsl:variable name="vMaxHours">
      <xsl:value-of select="max(/root/WorkSite[@Site=$vSiteId]/Hours)"/>
    </xsl:variable>
    <site id="{@id}">
      <xsl:apply-templates/>
      <xsl:for-each select="/root/WorkSite[Hours=$vMaxHours]/@Person">
        <person>
          <xsl:value-of select="."/>
        </person>        
      </xsl:for-each>
    </site>
  </xsl:template>

  <xsl:template match="WorkSite"/>

</xsl:stylesheet>

XML output:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <site id="S1">
      <othernodes/>
      <person>P1</person>
   </site>
   <site id="S2">
      <othernodes/>
      <person>P2</person>
   </site>
   <site id="S3">
      <othernodes/>
      <person>P2</person>
   </site>
</root>
DevNull
A: 

This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kSiteByName" match="@Site" use="."/>

 <xsl:key name="kWorksiteBySite"
   match="WorkSite" use="@Site"/>

 <xsl:key name="kSiteChildrenBySiteid"
  match="Site/node()" use="../@id"/>

 <xsl:variable name="vSites" select=
  "/*/*/@Site[generate-id()
             =
              generate-id(key('kSiteByName',.)[1])
              ]"
  />

 <xsl:template match="/">
  <root>
    <xsl:for-each select="$vSites">
      <xsl:for-each select="key('kWorksiteBySite', .)">
        <xsl:sort select="Hours" data-type="number"
         order="descending"/>
        <xsl:if test="position()=1">
         <site id="{@Site}">
            <xsl:copy-of select="key('kSiteChildrenBySiteid', @Site)"/>
            <person><xsl:value-of select="@Person"/></person>
         </site>
        </xsl:if>
      </xsl:for-each>
    </xsl:for-each>
  </root>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<root>
    <Site id="S1">
        <othernodes></othernodes>
    </Site>
    <Site id="S2">
        <othernodes></othernodes>
    </Site>
    <Site id="S3">
        <othernodes></othernodes>
    </Site>
    <WorkSite Person="P1" Site="S1">
        <Hours>8</Hours>
    </WorkSite>
    <WorkSite Person="P1" Site="S2">
        <Hours>2</Hours>
    </WorkSite>
    <WorkSite Person="P1" Site="S3">
        <Hours>20</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S1">
        <Hours>6</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S2">
        <Hours>10</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S3">
        <Hours>21</Hours>
    </WorkSite>
</root>

produces the wanted, correct result:

<root>
   <site id="S1">
      <othernodes/>
      <person>P1</person>
   </site>
   <site id="S2">
      <othernodes/>
      <person>P2</person>
   </site>
   <site id="S3">
      <othernodes/>
      <person>P2</person>
   </site>
</root>

Do note:

  1. The use of the Muenchian method for grouping to find all different Site values.

  2. The way maximum is found by sorting in descending order and getting the first result from the sorted node-list. This is much more efficient (O(N*log(N)) than scanning the sequence of nodes multiple times (comparing each value to every value in the list of values), which has O(N^2) time complexity.

Dimitre Novatchev