Soham Shah
Soham Shah

Reputation: 571

XSLT to remove duplicate while concating

My XML looks like following:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<BATCHES>
    <item>
        <Material>1000000079</Material>
        <Description>330 Bulk</Description>
        <Tank>T123</Tank>
        <Batch>2013225287</Batch>
    </item>
    <item>
        <Material>1000000079</Material>
        <Description>330 Bulk</Description>
        <Tank>T123</Tank>
        <Batch>2013225301</Batch>
    </item>
    <item>
        <Material>1000000196</Material>
        <Description>340R Bulk</Description>
        <Tank>T700</Tank>
        <Batch>1000188378</Batch>
    </item>
    <item>
        <Material>1000002754</Material>
        <Description>43 Bulk</Description>
        <Tank>T515</Tank>
        <Batch>2013180125</Batch>
    </item>
    <item>
        <Material>1000002754</Material>
        <Description>43 Bulk</Description>
        <Tank>T515</Tank>
        <Batch>2013203124</Batch>
    </item>
    <item>
        <Material>1000002754</Material>
        <Description>43 Bulk</Description>
        <Tank>T515</Tank>
        <Batch>2013214839</Batch>
    </item>
    <item>
        <Material>1000002754</Material>
        <Description>OGA 72043 Bulk</Description>
        <Tank>T517</Tank>
        <Batch>2013214342</Batch>
    </item>
</BATCHES>

My XSLT looks like follwoing:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>
    <xsl:template match="/">
        <Rowsets>
            <Rowset>
                <xsl:variable name="materials" select=".//item[Tank!='RECV' and Tank!='PARK'] "/>
                <xsl:for-each select="$materials">
                    <xsl:if test="generate-id(.)=                       generate-id($materials[Material=current()/Material])">
                        <Row>
                            <Material>
                                <xsl:value-of select="Material"/>
                            </Material>
                            <Description>
                                <xsl:value-of select="Description"/>
                            </Description>
                            <Value>
                                <xsl:for-each select="$materials[Material=current()/Material]/Tank">
                                    <xsl:if test="node()">
                                        <xsl:value-of select="concat(.,'||')"/>
                                    </xsl:if>
                                </xsl:for-each>
                            </Value>
                        </Row>
                    </xsl:if>
                </xsl:for-each>
            </Rowset>
        </Rowsets>
    </xsl:template>
</xsl:stylesheet>

The result of XSLT is:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Rowsets>
    <Rowset>
        <Row>
            <Material>1000000079</Material>
            <Description>330 Bulk</Description>
            <Value>T123||T123||</Value>
        </Row>
        <Row>
            <Material>1000000196</Material>
            <Description>340R Bulk</Description>
            <Value>T700||</Value>
        </Row>
        <Row>
            <Material>1000002754</Material>
            <Description>43 Bulk</Description>
            <Value>T515||T515||T515||T517||</Value>
        </Row>
    </Rowset>
</Rowsets>

My problem is in the field I dont want to concatenate Tank if it is repeated.

Sample should be:

<Row>
                <Material>1000002754</Material>
                <Description>43 Bulk</Description>
                <Value>T515||T517||</Value>
            </Row>

I tried various combinations but could not achieve it. Can anyone plz help?

Upvotes: 0

Views: 132

Answers (2)

adhocgeek
adhocgeek

Reputation: 1457

For XSLT 1.0, you want to be using something called Muenchian grouping. I made an assumption that you might want to keep the alternative descriptions too and simplified your XSLT to this :

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>

  <xsl:template match="/">
    <xsl:variable name="materials" select="BATCHES/item[Tank!='RECV' and Tank!='PARK']"/>

    <Rowsets>
      <Rowset>
        <xsl:for-each select="$materials[not(Material=preceding-sibling::item/Material)]">
          <Row>
            <xsl:variable name="items" select="//item[Material=current()/Material]"/>
            <xsl:variable name="distinct-descriptions" select="$items[not(Description=preceding-sibling::item/Description)]"/>
            <xsl:variable name="distinct-tanks" select="$items[not(Tank=preceding-sibling::item/Tank)]"/>

            <Material><xsl:value-of select="Material"/></Material>
            <Description>
              <xsl:for-each select="$distinct-descriptions">
                  <xsl:if test="position() != 1">||</xsl:if>
                  <xsl:value-of select="Description"/>
              </xsl:for-each>
            </Description>
            <Value>
              <xsl:for-each select="$distinct-tanks">
                <xsl:if test="position() != 1">||</xsl:if>
                <xsl:value-of select="Tank"/>
              </xsl:for-each>
            </Value>
          </Row>
        </xsl:for-each>
      </Rowset>
    </Rowsets>
  </xsl:template>
</xsl:stylesheet>

Does this do what you something like what you wanted?

Upvotes: 1

hr_117
hr_117

Reputation: 9627

If the XML getting bigger a key based solution (with xslt-1.0) would be better.

But to keel the solution mostly as it is, only have unique Tank value. You can add two templates:

<xsl:template match="item" mode="tank" />
<xsl:template match="item[not(Material = preceding::item/Material and Tank =  preceding::item/Tank)]" mode="tank" >
    <xsl:value-of select="Tank"/>
    <xsl:text>||</xsl:text>
</xsl:template>

This ignores any item which has same Material and same Tank value.

And apply this templates in replacement for the for-each loop in <Value>:

<xsl:apply-templates select="$materials[Material=current()/Material ]" mode="tank" />

Therefore try this:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>
    <xsl:template match="/">
        <Rowsets>
            <Rowset>
                <xsl:variable name="materials" select=".//item[Tank!='RECV' and Tank!='PARK'] "/>
                <xsl:for-each select="$materials">
                    <xsl:if test="generate-id(.)= generate-id($materials[Material=current()/Material])">
                        <Row>
                            <Material>
                                <xsl:value-of select="Material"/>
                            </Material>
                            <Description>
                                <xsl:value-of select="Description"/>
                            </Description>
                            <Value>
                                <xsl:apply-templates select="$materials[Material=current()/Material ]" mode="tank" />
                            </Value>
                        </Row>
                    </xsl:if>
                </xsl:for-each>
            </Rowset>
        </Rowsets>
    </xsl:template>
    <xsl:template match="item" mode="tank" />
    <xsl:template match="item[not(Material = preceding::item/Material and Tank =  preceding::item/Tank)]" mode="tank" >
        <xsl:value-of select="Tank"/>
        <xsl:text>||</xsl:text>
    </xsl:template>

</xsl:stylesheet>

Which will generate the following output:

<Rowsets>
  <Rowset>
    <Row>
      <Material>1000000079</Material>
      <Description>330 Bulk</Description>
      <Value>T123||</Value>
    </Row>
    <Row>
      <Material>1000000196</Material>
      <Description>340R Bulk</Description>
      <Value>T700||</Value>
    </Row>
    <Row>
      <Material>1000002754</Material>
      <Description>43 Bulk</Description>
      <Value>T515||T517||</Value>
    </Row>
  </Rowset>
</Rowsets>

Upvotes: 1

Related Questions