XSLT Duplicate Removal

I have an xml data source that looks like this:

<dsQueryResponse> <Department> <Rows> <Row dept="HR" state="NY" region="East"/> <Row dept="HR" state="NJ" region="East"/> <Row dept="SD" state="NY" region="East"/> <Row dept="MM" state="NY" region="East"/> <Row dept="SD" state="NJ" region="East"/> <Row dept="SD" state="CO" region="West"/> <Row dept="MM" state="CO" region="West"/> </Rows> </Department> </dsQueryResponse> 

My XSLT looks like this:

 <?xml version="1.0" encoding="utf-8"?> <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="no"/> <xsl:decimal-format NaN=""/> <xsl:param name="DeptQS">East</xsl:param> <xsl:variable name="deptRows" select="/dsQueryResponse/Department/Rows/Row[@region = $DeptQS]"/> <xsl:template match="/"> <xsl:if test="count($deptRows) &gt; 0"> <xsl:call-template name="deptList"/> </xsl:if> </xsl:template> <xsl:template name="deptList"> <xsl:for-each select="$deptRows"> <!-- process unique depts--> </xsl:for-each> </xsl:template> </xsl:stylesheet> 

I want all departments in the region to be discharged. The code I have will write out duplicate departments. But how can I write out a department for a region only once?

Thanks!

+4
source share
4 answers

For XSLT 1.0 you can use the " Muenchian Method ".

You will create a key to index Row elements with a combination of @region and @dept .

Then you get the first match of this region / department combination (having the desired area ( $DeptQS )) and print the department ( dept ).

Here is an example style sheet that displays <test> elements to display context:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/> <xsl:strip-space elements="*"/> <xsl:key name="kDept" match="Department/Rows/Row" use="concat(@region,'|',@dept)"/> <xsl:param name="DeptQS">East</xsl:param> <xsl:template match="/*"> <xsl:for-each select="Department/Rows/Row[count(.|key('kDept', concat($DeptQS,'|',@dept))[1])=1]"> <test>Current dept: <xsl:value-of select="@dept"/></test> </xsl:for-each> </xsl:template> </xsl:stylesheet> 

Here is the output using your input XML:

 <test>Current dept: HR</test> <test>Current dept: SD</test> <test>Current dept: MM</test> 
+2
source

You did not specify the desired result. But in XSLT 2.0 you can do something like this:

 <xsl:template match="Rows"> <xsl:for-each-group select="Row" group-by="@region"> <region name="{current-grouping-key()}"> <xsl:for-each-group select="current-group()" group-by="@dept"> <dept name="{current-grouping-key()}"> <xsl:for-each select="current-group()"> <state name="{@state}"/> </xsl:for-each> </dept> </xsl:for-each-group> </region> </xsl:for-each> </xsl:template> 
+4
source

How can I write out a department for an area only once?

This style sheet:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:param name="pRegion" select="'East'"/> <xsl:key name="kRowByDept-Region" match="Row" use="concat(@dept,'++',@region)"/> <xsl:template match="Rows"> <xsl:apply-templates select="Row[generate-id() = generate-id( key('kRowByDept-Region', concat(@dept,'++',$pRegion) )[1] ) ]"/> </xsl:template> <xsl:template match="Row"> <xsl:value-of select="concat(@dept,'&#xA;')"/> </xsl:template> </xsl:stylesheet> 

Output:

 HR SD MM 
+2
source

The following stylesheet shows a general approach to grouping at several levels:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="byRegion" match="Row" use="@region" /> <xsl:key name="byRegionState" match="Row" use="concat(@region, '|', @state)" /> <xsl:key name="byRegionStateDept" match="Row" use="concat(@region, '|', @state, '|', @dept)" /> <xsl:template match="Row[generate-id() = generate-id(key('byRegion', @region)[1])]"> <region name="{@region}"> <xsl:apply-templates select="key('byRegion', @region) [generate-id() = generate-id(key('byRegionState', concat(@region, '|', @state))[1])]" mode="states" /> </region> </xsl:template> <xsl:template match="Row" mode="states"> <state name="{@state}"> <xsl:apply-templates select="key('byRegionState', concat(@region, '|', @state)) [generate-id() = generate-id(key('byRegionStateDept', concat(@region, '|', @state, '|', @dept))[1])]" mode="dept" /> </state> </xsl:template> <xsl:template match="Row" mode="dept"> <dept><xsl:value-of select="@dept" /></dept> </xsl:template> </xsl:stylesheet> 

It outputs the following result to your conclusion:

 <region name="East"> <state name="NY"> <dept>HR</dept> <dept>SD</dept> <dept>MM</dept> </state> <state name="NJ"> <dept>HR</dept> <dept>SD</dept> </state> </region> <region name="West"> <state name="CO"> <dept>SD</dept> <dept>MM</dept> </state> </region> 
+1
source

Source: https://habr.com/ru/post/1346172/


All Articles