Selecting Unique Elements Using XSLT

I have the following XML:

<option> <title>ABC</title> <desc>123</desc> </option> <option> <title>ABC</title> <desc>12345</desc> </option> <option> <title>ABC</title> <desc>123</desc> </option> <option> <title>EFG</title> <desc>123</desc> </option> <option> <title>EFG</title> <desc>456</desc> </option> 

Using XSLT, I want to convert it to:

 <choice> <title>ABC</title> <desc>123</desc> <desc>12345</desc> </choice> <choice> <title>EFG</title> <desc>123</desc> <desc>456</desc> </choice> 
+4
source share
3 answers

I would suggest exploring the "grouping" to solve this problem. Either the built-in group functions of XSLT 2.0, as for each group, or, if you use XSLT 1, a method called "Muenchian Grouping".

+2
source

Here is a minimal XSLT 2.0 solution :

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="/*"> <choices> <xsl:for-each-group select="*/title" group-by="."> <choice> <title> <xsl:sequence select="current-grouping-key()"/> </title> <xsl:for-each-group select="current-group()/../desc" group-by="."> <xsl:sequence select="."/> </xsl:for-each-group> </choice> </xsl:for-each-group> </choices> </xsl:template> </xsl:stylesheet> 

Note the use of the current-group() and current-grouping-key() functions

+1
source

You have already received good answers. In the pursuit of brevity, I present my 16-line solution based on Dimitris's answer :

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="/*"> <choices> <xsl:for-each-group select="option" group-by="title"> <choice> <xsl:sequence select="title"/> <xsl:for-each-group select="current-group()/desc" group-by="."> <xsl:sequence select="."/> </xsl:for-each-group> </choice> </xsl:for-each-group> </choices> </xsl:template> </xsl:stylesheet> 

Note that the current node context inside for-each-group is the first element in the current group, and current-group() returns a list of all the elements in the current group. I use the fact that the title element is identical for input and output and copies the first title from each group.

And for completeness, the XSLT 1.0 solution using Muenchian grouping (20 lines):

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:key name="title" match="option/title" use="."/> <xsl:key name="desc" match="option/desc" use="."/> <xsl:template match="/*"> <choices> <xsl:for-each select="option/title[count(.|key('title',.)[1]) = 1]"> <choice> <xsl:copy-of select="."/> <xsl:for-each select="key('title',.)/../desc [count(.|key('desc', .)[../title=current()][1]) = 1]"> <xsl:copy-of select="."/> </xsl:for-each> </choice> </xsl:for-each> </choices> </xsl:template> </xsl:stylesheet> 
0
source

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


All Articles