0

I want to remove the duplicate values from the concatenated string.

Input is:

 <?xml version="1.0" encoding="ISO-8859-1"?>    
    <QL>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Device</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Dev</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Device</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>1234</SERIAL>
            <PROD_NAME>45 Mbps</PROD_NAME>
        </QITEM>
    </QL>
     <QL>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Device</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Dev</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Device</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>1234</SERIAL>
            <PROD_NAME>45 Mbps</PROD_NAME>
        </QITEM>
    </QL>

I want to concatenate the values and the output should be like:

<Result>
  <SERIAL>123,1234</SERIAL>
  <PROD_NAME>User/Dev,User/Device,45 Mbps</PROD_NAME>
</Result>
<Result>
  <SERIAL>123,1234</SERIAL>
  <PROD_NAME>User/Dev,User/Device,45 Mbps</PROD_NAME>
</Result>

So far, I have tried to achieve this with the following template:

<xsl:template name="join">
    <xsl:param name="list"/>
    <xsl:param name="separator"/>
    <xsl:for-each select="$list">
      <xsl:value-of select="."/>
      <xsl:if test="position() != last()">
        <xsl:value-of select="$separator"/>
      </xsl:if>
    </xsl:for-each>
</xsl:template>

This is giving the values separated by comma.
But I want to get the unique values.

1 Answers1

1

Unique/distinct values in pure XSLT 1.0 is best achieved by using the Muenchian Method of grouping.

In the example below, we create an xsl:key for each one of the items we want to have a group of.

For example, the key serial is a group of all SERIAL elements using the value of itself as the grouping key.

Then we loop over each group (xsl:for-each) and only select the first occurrence of that group.

Full example...

XML Input

<QL>
    <QITEM>
        <SERIAL>123</SERIAL>
        <PROD_NAME>User/Device</PROD_NAME>
    </QITEM>
    <QITEM>
        <SERIAL>123</SERIAL>
        <PROD_NAME>User/Dev</PROD_NAME>
    </QITEM>
    <QITEM>
        <SERIAL>123</SERIAL>
        <PROD_NAME>User/Device</PROD_NAME>
    </QITEM>
    <QITEM>
        <SERIAL>1234</SERIAL>
        <PROD_NAME>45 Mbps</PROD_NAME>
    </QITEM>
</QL>

XSLT 1.0

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

  <xsl:key name="serial" match="SERIAL" use="."/>
  <xsl:key name="prodname" match="PROD_NAME" use="."/>

  <xsl:template match="/*">
    <Result>
      <SERIAL>
        <xsl:for-each select="QITEM/SERIAL[count(.|key('serial',.)[1])=1]">
          <xsl:if test="position() > 1">,</xsl:if>
          <xsl:value-of select="."/>
        </xsl:for-each>
      </SERIAL>
      <PROD_NAME>
        <xsl:for-each select="QITEM/PROD_NAME[count(.|key('prodname',.)[1])=1]">
          <xsl:if test="position() > 1">,</xsl:if>
          <xsl:value-of select="."/>
        </xsl:for-each>
      </PROD_NAME>
    </Result>
  </xsl:template>

</xsl:stylesheet>

XML Output

<Result>
   <SERIAL>123,1234</SERIAL>
   <PROD_NAME>User/Device,User/Dev,45 Mbps</PROD_NAME>
</Result>

UPDATE

I'm not sure I completely understand your update, but what I think you can do is create a compound key using the generated id of the ancestor QL.

XML Input (Wrapped in doc to make it well-formed.)

<doc>
    <QL>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Device</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Dev</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Device</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>1234</SERIAL>
            <PROD_NAME>45 Mbps</PROD_NAME>
        </QITEM>
    </QL>
    <QL>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Device</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Dev</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>123</SERIAL>
            <PROD_NAME>User/Device</PROD_NAME>
        </QITEM>
        <QITEM>
            <SERIAL>1234</SERIAL>
            <PROD_NAME>45 Mbps</PROD_NAME>
        </QITEM>
    </QL>
</doc>

XSLT 1.0

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

  <xsl:key name="serial" match="SERIAL" use="concat(generate-id(ancestor::QL),'|',.)"/>
  <xsl:key name="prodname" match="PROD_NAME" use="concat(generate-id(ancestor::QL),'|',.)"/>

  <xsl:template match="QL">
    <Result>
      <SERIAL>        
        <xsl:for-each select="QITEM/SERIAL[count(.|key('serial',concat(generate-id(ancestor::QL),'|',.))[1])=1]">
          <xsl:if test="position() > 1">,</xsl:if>
          <xsl:value-of select="."/>
        </xsl:for-each>
      </SERIAL>
      <PROD_NAME>
        <xsl:for-each select="QITEM/PROD_NAME[count(.|key('prodname',concat(generate-id(ancestor::QL),'|',.))[1])=1]">
          <xsl:if test="position() > 1">,</xsl:if>
          <xsl:value-of select="."/>
        </xsl:for-each>
      </PROD_NAME>
    </Result>
  </xsl:template>

</xsl:stylesheet>

XML Output

<Result>
   <SERIAL>123,1234</SERIAL>
   <PROD_NAME>User/Device,User/Dev,45 Mbps</PROD_NAME>
</Result>
<Result>
   <SERIAL>123,1234</SERIAL>
   <PROD_NAME>User/Device,User/Dev,45 Mbps</PROD_NAME>
</Result>
Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
  • Yes this is working fine but the fact is i have the above input in repeating .. then the values 123,1234 are not visible if they are coming in the next loop again. – srinivas kalyan Feb 07 '17 at 04:29
  • I have updated the Question.Please look into it. I have added the input and the output also. – srinivas kalyan Feb 07 '17 at 04:33
  • @srinivaskalyan - Please see my update. If that doesn't answer your question, you may want to un-accept my answer so other people will look at your question. Right now it has an accepted answer so most people will skip past it. – Daniel Haley Feb 07 '17 at 06:25
  • 1
    @DanielHaley - Thanks for the solution. It is working fine with the updated one. – srinivas kalyan Feb 07 '17 at 17:54