0

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.

  • mario
    • 1
    • 2
    • You have tagged this Muenchian grouping, so why is this a question? – michael.hor257k May 13 '14 at 08:32
    • And your XML input is invalid. – michael.hor257k May 13 '14 at 09:30
    • I need XSLT 1.0 to do this transformation. Any help would be much appreciated! – mario May 13 '14 at 13:14
    • I suggest you read the [standard article](http://www.jenitennison.com/xslt/grouping/muenchian.html). Then if you have any **specific** questions about your implementation, ask them. – michael.hor257k May 13 '14 at 14:38
    • I know the standard article, but this implementation is very specific, so i need some help. – mario May 14 '14 at 07:58
    • "*3. Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results.*" http://stackoverflow.com/help/on-topic – michael.hor257k May 14 '14 at 08:03
    • This is progress, but I am afraid I don't understand your rules for grouping. Could you please edit your question and expand on that? It seems like you have two levels of grouping: (1) by Price; (2) by common legs - but when I do that, I am not getting the same result as you do. – michael.hor257k May 14 '14 at 13:07
    • the Legs in the Box are the pair, one od them has group=0 second=1, for examle arrval and departure. I would like to group Legs from Group=0 with Legs from Group=1 which have the same price and is possible to join by the Key in Leg, see pair in Boxes – mario May 14 '14 at 13:20
    • I am afraid that's still not clear. Pleas have a look at my answer and see if it's anywhere close to your request. – michael.hor257k May 14 '14 at 13:31
    • Thank You very much for Your help, but the output must be the same like in question. I will try to explain the rules. In each group gruping by price can by only legs we can pair. The pair is 2 Legs, one from Group=0 and the second Group=1, see input – mario May 14 '14 at 14:10
    • I am sorry, I don't understand your explanation. I suggest you edit your question and add a step-by-step worked out example (perhaps using a simpler data set) of how you get from input to output. – michael.hor257k May 14 '14 at 14:48
    • Look on Your result: ` ` - this Grup is corect, because from the result i have make the same pair like in input. The pair: for each Leg Group=0 one Leg Group=1. From the Group over I can make two pairs: ` ` and ' ` and it is corect. – mario May 14 '14 at 18:11
    • the step-by-step examle atached – mario May 15 '14 at 10:48

    2 Answers2

    1

    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>
    
    michael.hor257k
    • 113,275
    • 6
    • 33
    • 51
    • Thank you for your help, im attached step-by-step rules for groupig, please help me with it. – mario May 17 '14 at 07:48
    0

    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>