mario
mario

Reputation: 1

XSLT group on multiple keys

This is the input file.

<XML>
<Box Price="541" Key="1">
<Leg Key="56T58T" Group="0"></Leg>
<Leg Key="177T179T" Group="1"></Leg>
</Box>
<Box Price="541" Key="2">
<Leg Key="128T130T" Group="0"></Leg>
<Leg Key="196T198T" Group="1"></Leg>
</Box>
<Box Price="541" Key="3">
<Leg Key="56T58T" Group="0"></Leg>
<Leg Key="196T198T" Group="1"></Leg>
</Box>
<Box Price="541" Key="4">
<Leg Key="128T130T" Group="0"></Leg>
<Leg Key="177T179T" Group="1"></Leg>
</Box>
<Box Price="541" Key="5">
<Leg Key="243T246T" Group="0"></Leg>
<Leg Key="60T63T" Group="1"></Leg>
</Box>
<Box Price="541" Key="6">
<Leg Key="243T246T" Group="0"></Leg>
<Leg Key="133T136T" Group="1"></Leg>
</Box>
<Box Price="700" Key="7">
<Leg Key="243T" Group="0"></Leg>
<Leg Key="133T136T" Group="1"></Leg>
</Box>
</XML>

The output is:

<Box Price="541">
<Leg Key="56T58T" Group="0"></Leg>
<Leg Key="128T130T" Group="0"></Leg>
<Leg Key="177T179T" Group="1"></Leg>
<Leg Key="196T198T" Group="1"></Leg>
</Box>
<Box Price="541">
<Leg Key="243T246T" Group="0"></Leg>
<Leg Key="133T136T" Group="1"></Leg>
<Leg Key="60T63T" Group="1"></Leg>
</Box>
<Box Price="700">
<Leg Key="243T" Group="0"></Leg>
<Leg Key="133T136T" Group="1"></Leg>
</Box>

Rules for Grouping

  • The price
  • The leg group=0 and gropup=1 is the pair. We can group leg when leg Group=0 have the same Key=in Group=1. For example Leg in Group=0 has Key=56T58T and two Keys (177T179T and 196T198T) in Group=1. The same two Keys (177T179T and 196T198T) has Leg in Group=0 Key=128T130T. So we can its grouping

    <Box Price="541">
    <Leg Key="56T58T" Group="0"></Leg>
    <Leg Key="128T130T" Group="0"></Leg>
    <Leg Key="177T179T" Group="1"></Leg>
    <Leg Key="196T198T" Group="1"></Leg>
    </Box>
    

    I am using XSLT 1.0. Any help would be GREATLY appreciated.



    I have xsl, but it group only by Price

    <xsl:output method="xml" indent="yes" />
    
    <xsl:key name="price" match="Box" use="@Price" />
    
    
    <xsl:template match="XML">
            <xsl:apply-templates select="Box[generate-id(.)=generate-id(key('price',@Price)[1])]"/>
    </xsl:template>
    
    
    <xsl:template match="Box">
        <Group Price="{@Price}">
            <xsl:for-each select="key('price', @Price)"> 
          <xsl:for-each select="Leg">  
                <Leg Key="{@Key}" Group="{@Group}"></Leg>
             </xsl:for-each>
            </xsl:for-each>
        </Group>
    </xsl:template>
    

    and the output:

    <?xml version="1.0"?>
    <Group Price="541">
     <Leg Key="56T58T" Group="0"/>
     <Leg Key="177T179T" Group="1"/>
     <Leg Key="128T130T" Group="0"/>
     <Leg Key="196T198T" Group="1"/>
     <Leg Key="56T58T" Group="0"/>
     <Leg Key="196T198T" Group="1"/>
     <Leg Key="128T130T" Group="0"/>
     <Leg Key="177T179T" Group="1"/>
     <Leg Key="243T246T" Group="0"/>
     <Leg Key="60T63T" Group="1"/>
     <Leg Key="243T246T" Group="0"/>
     <Leg Key="133T136T" Group="1"/>
    </Group><Group Price="700">
      <Leg Key="243T" Group="0"/>
      <Leg Key="133T136T" Group="1"/>
    </Group>
    

    Any help would be GREATLY appreciated.

    Step by step Grouping.

    1. Group by the same Price and the same Key for Leg Group=0

      <Group Price="541"> <Leg Key="56T58T" Group="0"></Leg> <Leg Key="177T179T" Group="1"></Leg> <Leg Key="56T58T" Group="0"></Leg> <Leg Key="196T198T" Group="1"></Leg> </Group> <Group Price="541"> <Leg Key="128T130T" Group="0"></Leg> <Leg Key="196T198T" Group="1"></Leg> <Leg Key="128T130T" Group="0"></Leg> <Leg Key="177T179T" Group="1"></Leg> </Group> <Group Price="541"> <Leg Key="243T246T" Group="0"></Leg> <Leg Key="60T63T" Group="1"></Leg> <Leg Key="243T246T" Group="0"></Leg> <Leg Key="133T136T" Group="1"></Leg> </Group> <Group Price="700"> <Leg Key="243T" Group="0"></Leg> <Leg Key="133T136T" Group="1"></Leg> </Group>

    2. In each Group remove duplicate Legs (the same Key)

      <Group Price="541" > <Leg Key="56T58T" Group="0"></Leg> <Leg Key="177T179T" Group="1"></Leg> <Leg Key="196T198T" Group="1"></Leg> </Group> <Group Price="541"> <Leg Key="128T130T" Group="0"></Leg> <Leg Key="196T198T" Group="1"></Leg> <Leg Key="177T179T" Group="1"></Leg> </Group> <Group Price="541"> <Leg Key="243T246T" Group="0"></Leg> <Leg Key="60T63T" Group="1"></Leg> <Leg Key="133T136T" Group="1"></Leg> </Group> <Group Price="700"> <Leg Key="243T" Group="0"></Leg> <Leg Key="133T136T" Group="1"></Leg> </Group>

    3. Group the same Price and the same Keys for Leg Group=1 (first an second Group have the same Legs Group=1, so we can it Gruop )

      <Group Price="541"> <Leg Key="56T58T" Group="0"></Leg> <Leg Key="177T179T" Group="1"></Leg> <Leg Key="196T198T" Group="1"></Leg> <Leg Key="128T130T" Group="0"></Leg> <Leg Key="196T198T" Group="1"></Leg> <Leg Key="177T179T" Group="1"></Leg> </Group> <Group Price="541"> <Leg Key="243T246T" Group="0"></Leg> <Leg Key="60T63T" Group="1"></Leg> <Leg Key="133T136T" Group="1"></Leg> </Group> <Group Price="700"> <Leg Key="243T" Group="0"></Leg> <Leg Key="133T136T" Group="1"></Leg> </Group>

    4. In each Group remove duplicate Legs (the same Key)

      <Group Price="541"> <Leg Key="56T58T" Group="0"></Leg> <Leg Key="128T130T" Group="0"></Leg> <Leg Key="177T179T" Group="1"></Leg> <Leg Key="196T198T" Group="1"></Leg> </Group> <Group Price="541"> <Leg Key="243T246T" Group="0"></Leg> <Leg Key="60T63T" Group="1"></Leg> <Leg Key="133T136T" Group="1"></Leg> </Group> <Group Price="700"> <Leg Key="243T" Group="0"></Leg> <Leg Key="133T136T" Group="1"></Leg> </Group>

    Finaly its the output.

    Upvotes: 0

    Views: 1379

  • Answers (2)

    Geert Bormans
    Geert Bormans

    Reputation: 1

    Interesting assignment...

    I think the answer is not as much in Muenchian Grouping as it is in recursion. You could follow the path of references for a first group and register the Keys of the path followed After you grouped all the Leg elements for that particulare path, you could move on to the next Box that is not yet "visited" to create the next group

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0">
    
        <xsl:key name="link" match="Leg" use="concat(parent::Box/@Price, '-', @Key, '-', @Group)"/>
    
        <xsl:output indent="yes"/>
    
        <xsl:template match="XML">
            <XML>
                <xsl:call-template name="process-group-node">
                    <xsl:with-param name="box" select="Box[1]"/>
                </xsl:call-template>
            </XML>
        </xsl:template>
    
        <xsl:template name="process-group-node">
            <xsl:param name="box"/>
            <xsl:param name="used-keys"/>
            <xsl:variable name="new-keys">
                <xsl:call-template name="search-path">
                    <xsl:with-param name="running-route" select="'#'"/>
                    <xsl:with-param name="current-leg" select="$box/Leg[1]"/>
                </xsl:call-template>
            </xsl:variable>
            <Box Price="{$box/@Price}">
                <xsl:for-each select="/XML/Box[contains($new-keys, concat('#', @Key, '#'))]/Leg[generate-id() = generate-id(key('link', concat(parent::Box/@Price, '-', @Key, '-', @Group) )[1])]">
                    <xsl:sort select="@Group" data-type="number" order="ascending"/>
                    <xsl:copy-of select="."/>
                </xsl:for-each>
            </Box>
            <xsl:variable name="new-concat-keys" select="concat($used-keys, $new-keys)"/>
            <xsl:if test="$box/following-sibling::Box[not(contains($new-concat-keys, concat('#', @Key, '#')))]">
                <xsl:call-template name="process-group-node">
                    <xsl:with-param name="box" select="$box/following-sibling::Box[not(contains($new-concat-keys, concat('#', @Key, '#')))]"/>
                    <xsl:with-param name="used-keys" select="$new-concat-keys"/>
                </xsl:call-template>
            </xsl:if>
        </xsl:template>
    
        <xsl:template name="search-path">
            <xsl:param name="running-route"/>
            <xsl:param name="current-leg"/>
            <xsl:choose>
                <xsl:when test="contains($running-route, concat('#', $current-leg/parent::Box/@Key, '#'))">
                    <xsl:value-of select="$running-route"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:variable name="this-id" select="generate-id($current-leg)"/>
                    <xsl:variable name="this-key" select="concat($current-leg/parent::Box/@Price, '-', $current-leg/@Key, '-', $current-leg/@Group)"/>
                    <xsl:variable name="next-equivalent-leg" select="key('link', $this-key)[not(generate-id() = $this-id)]"/>
                    <xsl:variable name="next-equivalent-id" select="generate-id($next-equivalent-leg)"/>
                    <xsl:variable name="next-leg" select="$next-equivalent-leg/parent::Box/Leg[not(generate-id() = $next-equivalent-id)]"/>
                    <xsl:call-template name="search-path">
                        <xsl:with-param name="running-route" select="concat($running-route, $current-leg/parent::Box/@Key ,'#')"/>
                        <xsl:with-param name="current-leg" select="$next-leg"></xsl:with-param>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    
    </xsl:stylesheet>
    

    Upvotes: 0

    michael.hor257k
    michael.hor257k

    Reputation: 116959

    In the interest of moving this forward, I am posting this tentative stylesheet that may or may not be close to what you want:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:key name="box-by-price" match="Box" use="@Price" />
    <xsl:key name="box-by-legs" match="Box" use="Leg/@Key" />
    <xsl:key name="leg-by-key" match="Leg" use="@Key" />
    
    <xsl:template match="XML">
        <root>
            <!-- (1) group by price -->
            <xsl:for-each select="Box[generate-id()=generate-id(key('box-by-price',@Price)[1])]">
                <!-- (2) within each price group, combine boxes that share a common leg (regardless of group) -->
                <xsl:for-each select="key('box-by-price',@Price)[generate-id()=generate-id(key('box-by-legs',Leg/@Key)[1])]">
                    <Group Price="{@Price}">
                        <!-- (3) in each group, list the unique legs of all the boxes in the group -->
                            <xsl:copy-of select="key('box-by-legs',Leg/@Key)/Leg[generate-id()=generate-id(key('leg-by-key',@Key)[1])]"/>
                    </Group>
                </xsl:for-each>
            </xsl:for-each>
        </root>
    </xsl:template>
    
    </xsl:stylesheet>
    

    When this is applied to your input:

    <?xml version="1.0" encoding="UTF-8"?>
    <XML>
       <Box Price="541" Key="1">
          <Leg Key="56T58T" Group="0"/>
          <Leg Key="177T179T" Group="1"/>
       </Box>
       <Box Price="541" Key="2">
          <Leg Key="128T130T" Group="0"/>
          <Leg Key="196T198T" Group="1"/>
       </Box>
       <Box Price="541" Key="3">
          <Leg Key="56T58T" Group="0"/>
          <Leg Key="196T198T" Group="1"/>
       </Box>
       <Box Price="541" Key="4">
          <Leg Key="128T130T" Group="0"/>
          <Leg Key="177T179T" Group="1"/>
       </Box>
       <Box Price="541" Key="5">
          <Leg Key="243T246T" Group="0"/>
          <Leg Key="60T63T" Group="1"/>
       </Box>
       <Box Price="541" Key="6">
          <Leg Key="243T246T" Group="0"/>
          <Leg Key="133T136T" Group="1"/>
       </Box>
       <Box Price="700" Key="7">
          <Leg Key="243T" Group="0"/>
          <Leg Key="133T136T" Group="1"/>
       </Box>
    </XML>
    

    the result is:

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
       <Group Price="541">
          <Leg Key="56T58T" Group="0"/>
          <Leg Key="177T179T" Group="1"/>
       </Group>
       <Group Price="541">
          <Leg Key="128T130T" Group="0"/>
          <Leg Key="196T198T" Group="1"/>
       </Group>
       <Group Price="541">
          <Leg Key="243T246T" Group="0"/>
          <Leg Key="60T63T" Group="1"/>
          <Leg Key="133T136T" Group="1"/>
       </Group>
    </root>
    

    Upvotes: 1

    Related Questions