XSLT: Calculate results for filtered subset and full set of data in one go

2.1k views Asked by At

My xslt-fu is still very weak. Early days.

My XML data is a list of companies, their service provider, and their value.

I've managed to group by service provider so I can see which service providers have the most market share by number of clients, and overall market value.

This works well for the whole market, but I'd also like to get values out for the "top 100 (by value) companies" as well. I have no idea how to add this.

Current XSLT (see comments for where I would like to add additional data):

<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal">
    <xsl:output method="html" indent="no"/>

    <xsl:key name="providerkey" match="/dsQueryResponse/Rows/Row" use="@svcprovider" />

    <xsl:template match="/">
        <table>
            <tr>
                <th>Service Provider</th>
                <th>Total Market value</th>
                <th>No. Cos</th>
                <th>Market value from top 100 cos</th>
                <th>No. cos from top 100</th>
            </tr>

            <xsl:for-each select="/dsQueryResponse/Rows/Row[generate-id(.)=generate-id(key('providerkey',@svcprovider)[1])]">
                <xsl:sort select="count(key('providerkey',@svcprovider))" order="descending" data-type="number" />
                <xsl:variable name="totalvalue" select="sum(/dsQueryResponse/Rows/Row[@svcprovider=current()/@svcprovider]/@covalue)" />
                <tr>
                    <td>
                        <xsl:value-of select="current()/@svcprovider"/>
                    </td> 
                    <td>
                        <xsl:value-of select="$totalvalue" />
                    </td> 
                    <td>
                        <xsl:value-of select="count(key('providerkey',@svcprovider))"/>
                    </td>
                    <td>
                        <!-- How do I get total value, but only from the top 100 companies ?? -->
                    </td>
                    <td>
                        <!-- How do I get total no. of companies, but only from the top 100 ?? -->
                    </td>                       

                </tr>
            </xsl:for-each>
        </table>
    </xsl:template>

</xsl:stylesheet>

Sample XML data is:

<Rows>
    <Row coname="client name 1" svcprovider="svc provider name 1" covalue="998" />
    <Row coname="client name 2" svcprovider="svc provider name 2" covalue="1081" />
    <!-- ...etc... -->

</Rows>

Obviously there are well over 100 rows. Basically I'm using this to calculate market share across the whole of the market, and would like to also calculate for the top-end.

I expect I need to add an additional sorting/filtering loop, but I'm not sure how to go about nesting it.

Thanks in advance

John

1

There are 1 answers

4
Dimitre Novatchev On BEST ANSWER

Try something like this:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="msxsl">
    <xsl:output method="html" indent="yes"/>
    <xsl:param name="pTopNumber" select="3"/>

    <xsl:key name="providerkey"
        match="/dsQueryResponse/Rows/Row" use="@svcprovider" />

    <xsl:variable name="vrtfTopCompanies">
     <xsl:for-each select="/*/*/Row">
       <xsl:sort select="covalue"
            data-type="number" order="descending"/>
       <xsl:if test="not(position() > $pTopNumber)">
        <xsl:copy-of select="."/>
       </xsl:if>
     </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="vTopRows"
         select="msxsl:node-set($vrtfTopCompanies)/*"/>

    <xsl:template match="/">
        <table>
            <tr>
                <th>Service Provider</th>
                <th>Total Market value</th>
                <th>No. Cos</th>
                <th>Market value from top 100 cos</th>
                <th>No. cos from top 100</th>
            </tr>
            <xsl:for-each select=
              "/dsQueryResponse/Rows/Row
                       [generate-id()
                       =
                       generate-id(key('providerkey',@svcprovider)[1])
                       ]">
                <xsl:sort select="count(key('providerkey',@svcprovider))"
                          order="descending" data-type="number" />
                <xsl:variable name="totalvalue" select=
                   "sum(/dsQueryResponse/Rows
                                /Row[@svcprovider=current()/@svcprovider]/@covalue)" />
                <tr>
                    <td>
                        <xsl:value-of select="current()/@svcprovider"/>
                    </td>
                    <td>
                        <xsl:value-of select="$totalvalue" />
                    </td>
                    <td>
                        <xsl:value-of select="count(key('providerkey',@svcprovider))"/>
                    </td>
                    <td>
                        <!-- How do I get total value, but only from the top 100 companies ?? -->
                        <xsl:value-of select="sum($vTopRows[@svcprovider=current()/@svcprovider]/@covalue)"/>
                    </td>
                    <td>
                        <!-- How do I get total no. of companies, but only from the top 100 ?? -->
                        <xsl:value-of select="count(key('providerkey',@svcprovider)[@coname = $vTopRows/@coname])"/>
                    </td>
                </tr>
            </xsl:for-each>
        </table>
    </xsl:template>
</xsl:stylesheet>

when applied on the following XML document:

<dsQueryResponse>
    <Rows>
        <Row coname="client name 1" svcprovider="svc provider name 1" covalue="998" />
        <Row coname="client name 2" svcprovider="svc provider name 2" covalue="1081" />
        <Row coname="client name 3" svcprovider="svc provider name 3" covalue="998" />
        <Row coname="client name 4" svcprovider="svc provider name 4" covalue="2081" />
        <Row coname="client name 5" svcprovider="svc provider name 5" covalue="3998" />
        <Row coname="client name 2" svcprovider="svc provider name 2" covalue="1081" />
        <Row coname="client name 1" svcprovider="svc provider name 1" covalue="998" />
        <Row coname="client name 2" svcprovider="svc provider name 2" covalue="1081" />
        <Row coname="client name 1" svcprovider="svc provider name 1" covalue="998" />
        <Row coname="client name 2" svcprovider="svc provider name 2" covalue="1081" />
    </Rows>
</dsQueryResponse>

the result is:

<table>
    <tr>
        <th>Service Provider</th>
        <th>Total Market value</th>
        <th>No. Cos</th>
        <th>Market value from top 100 cos</th>
        <th>No. cos from top 100</th>
    </tr>
    <tr>
        <td>svc provider name 2</td>
        <td>4324</td>
        <td>4</td>
        <td>1081</td>
        <td>4</td>
    </tr>
    <tr>
        <td>svc provider name 1</td>
        <td>2994</td>
        <td>3</td>
        <td>998</td>
        <td>3</td>
    </tr>
    <tr>
        <td>svc provider name 3</td>
        <td>998</td>
        <td>1</td>
        <td>998</td>
        <td>1</td>
    </tr>
    <tr>
        <td>svc provider name 4</td>
        <td>2081</td>
        <td>1</td>
        <td>0</td>
        <td>0</td>
    </tr>
    <tr>
        <td>svc provider name 5</td>
        <td>3998</td>
        <td>1</td>
        <td>0</td>
        <td>0</td>
    </tr>
</table>