views:

91

answers:

2

I am new to Stack Overflow and to XSLT. I have a problem with removing duplicate entries from the output, based on some child element conditions.

Here is an example of XML that I have:

<partyorders>    
    <order>  
        <day>12</day>
        <month>05</month>  
        <year>2000</year>  
        <amount>5000.00</amount>  
        <decision>pending</decision>  
    </order>  
    <order>  
        <day>19</day>  
        <month>04</month>  
        <year>2000</year>  
        <amount>2000.00</amount>  
        <decision>pending</decision>  
    </order>  
    <order>  
        <day>19</day>  
        <month>04</month>  
        <year>2000</year>  
        <amount>2000.00</amount>  
        <decision>reserved</decision>  
    </order> 
     <order>  
        <day>05</day>  
        <month>04</month>  
        <year>2000</year>  
        <amount>1000.00</amount>  
        <decision>pending</decision>  
    </order>  
    <order>  
        <day>05</day>  
        <month>04</month>  
        <year>2000</year>  
        <amount>1000.00</amount>  
        <decision>reserved</decision>  
    </order> 
    <order>
        ....
    </order>
</partyorders>

.... represents more orders of same as above

I need to get the output as:

  1. only one order with pending/approved status.
  2. if there are two orders with only different in status, i want to display reserved order only after confirming by comparing elements <day>, <month>, <year>, <amount>

I want the following output based on <day>, <month>, <year>, <amount> and <decision> tags:

<restrntpartyorders>    
    <restrntorder>  
        <partyday>12</partyday>
        <partymonth>05</partymonth>  
        <partyyear>2000</partyyear>  
        <partyamount>5000.00</partyamount>  
        <partydecision>pending</partydecision>  
    </restrntorder>  
    <restrntorder>  
        <partyday>19</partyday>  
        <partymonth>04</partymonth>  
        <partyyear>2000</partyyear>  
        <partyamount>2000.00</partyamount>  
        <partydecision>reserved</partydecision>  
    </restrntorder>  
    <restrntorder>  
        <partyday>05</partyday>  
        <partymonth>04</partymonth>  
        <partyyear>2000</partyyear>  
        <partyamount>1000.00</partyamount>  
        <partydecision>reserved</partydecision>  
    </restrntorder> 
    <restrntorder>
        ....
    </restrntorder>
</restrntpartyorders>

Can someone help me out in getting the solution using XSLT 1.0?

A: 

The relevant xsl:template contents would be:

<partyorders>
  <xsl:for-each select="/partyorders/order">
    <xsl:if test="count(following-sibling::order[day = current()/day and month = current()/month etc etc]) = 0">
      <xsl:copy-of select="." />
    </xsl:if>
  </xsl:for-each>
</partyorders>

It's O(N^2) tho, and I won't recommend using XSLT for that under load.

alamar
You can certainly use XSLT for that. You just can't use *that* XSLT. The Muenchian technique that Tomalak's answer employes is O(N).
Robert Rossney
You're probsbly right.
alamar
+1  A: 
<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output indent="yes" encoding="utf-8" />

  <!-- index order elements by year, month, day, amount -->
  <xsl:key 
    name="kOrdersByDate" 
    match="order" 
    use="concat(year, '-', month, '-', day, '$', amount)"
  />

  <xsl:template match="order">
    <!-- select all orders of same date and amount -->
    <xsl:variable name="same" select="
      key('kOrdersByDate', concat(year, '-', month, '-', day, '$', amount))
    " />
    <!-- of those, select the first 'reserved' order -->
    <xsl:variable name="same-reserved" select="
      $same[decision = 'reserved']
    " />

    <!-- check that either: 
      1. no other equal order, e.g. count($same) = 1, or
      2. reseverd order exists, take it
      3. no reseverd order exists, take the first "same" order
    -->
    <xsl:if test="
      count($same) = 1 
      or 
      ($same-reserved and generate-id() = generate-id($same-reserved[1]))
      or
      (not($same-reserved) and generate-id() = generate-id($same[1]))
    ">
      <!-- rename order -> restrntorder -->
      <restrntorder>
        <xsl:apply-templates select="*" />
      </restrntorder>
    </xsl:if>
  </xsl:template>

  <!-- rename partyorders -> restrntpartyorders -->
  <xsl:template match="partyorders">
    <restrntpartyorders>
      <xsl:apply-templates select="order" />
    </restrntpartyorders>
  </xsl:template>

  <!-- rename day etc. -> partyday etc. -->
  <xsl:template match="order/*">
    <xsl:element name="party{local-name()}">
      <xsl:value-of select="." />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

Output:

<?xml version="1.0" encoding="utf-8"?>
<restrntpartyorders>
    <restrntorder>
        <partyday>12</partyday>
        <partymonth>05</partymonth>
        <partyyear>2000</partyyear>
        <partyamount>5000.00</partyamount>
        <partydecision>pending</partydecision>
    </restrntorder>

    <restrntorder>
        <partyday>19</partyday>
        <partymonth>04</partymonth>
        <partyyear>2000</partyyear>
        <partyamount>2000.00</partyamount>
        <partydecision>reserved</partydecision>
    </restrntorder>

    <restrntorder>
        <partyday>05</partyday>
        <partymonth>04</partymonth>
        <partyyear>2000</partyyear>
        <partyamount>1000.00</partyamount>
        <partydecision>reserved</partydecision>
    </restrntorder>
    <restrntorder>
        ....
    </restrntorder>
</restrntpartyorders>
Tomalak
Thank you Tomalak. I have questions 1. What if i want to filter also the orders with rejected status. 2. If i need to customize the outpout..ie the output elements can be <mypartyday/> instead of <partyday/> and <status> instead of <partydecision/>. Please help
angi