Selecting the first node of a Sorted Group

Discussion in 'XML' started by IcedDante, Dec 10, 2006.

  1. IcedDante

    IcedDante Guest

    Working with a sorted group, the inability to use following-sibling
    (which uses Document Order) and convert an RTF (not avaible with the
    Parser that we are using) hampered our ability to solve the following
    problem. Consider the following set of Data:

    <example>
    <Credentials time="3">
    <UserId>kiss</UserId>
    </Credentials>
    <Credentials time="1" break="true">
    <UserId>bob</UserId>
    </Credentials>
    <Credentials time="6" break="true">
    <UserId>my</UserId>
    </Credentials>
    <Credentials time="0">
    <UserId>Look,</UserId>
    </Credentials>
    <Credentials time="2">
    <UserId>can</UserId>
    </Credentials>
    <Credentials time="9">
    <UserId>tookish</UserId>
    </Credentials>
    </example>

    When sorted in ascending order, the data reads: "Look, bob can kiss my
    tookish". For our requirements I am processing it as a descending set:
    "tookish my kiss can bob Look,"

    That is the first requirement. However, in the even that a node with
    the property of "break" equal to "true" is found, processing should
    halt (multiple nodes can have the break property, but we really only
    care about the first one).
    So the output should read: "tookish my"

    I devised a solution by using the substring-before operator to select
    the first break node's sorted position.

    <xsl:param name="breakPos">
    <xsl:for-each select="example/Credentials">
    <xsl:sort select="@time" order="descending" />
    <xsl:call-template name="countSequence">
    </xsl:call-template>
    </xsl:for-each>
    </xsl:param>
    <xsl:param name="endElem">
    <xsl:value-of select="substring-before($breakPos,'|')" />
    </xsl:param>

    <xsl:template match="/">
    <html>
    <body>
    <xsl:for-each select="example/Credentials">
    <xsl:sort select="@time" order="descending" />
    <xsl:if test="($endElem = '') or (position() &lt;= $endElem)">
    <xsl:value-of select="UserId" /><br />
    </xsl:if>
    </xsl:for-each>
    </body>
    </html>
    </xsl:template>

    Yeah, this renders the html output:
    tookish<br />
    my<br />

    but I felt like the "endElem" parameter derivation was kind of a hack.
    Would there be a better solution- possibly using Meunchian grouping to
    perform this fix?
    IcedDante, Dec 10, 2006
    #1
    1. Advertising

  2. IcedDante

    IcedDante Guest

    Whoops... I forgot the countSequence template. It basically builds the
    list of nodes that have break="true" based on their position in the
    sorted tree, delimited by the '|' symbol:

    <xsl:template name="countSequence">
    <xsl:choose>
    <xsl:when test="(@break = 'true')">
    <xsl:value-of select="position()" /><xsl:text>|</xsl:text>
    </xsl:when>
    <xsl:eek:therwise></xsl:eek:therwise>
    </xsl:choose>
    </xsl:template>

    On Dec 9, 9:34 pm, "IcedDante" <> wrote:
    > Working with a sorted group, the inability to use following-sibling
    > (which uses Document Order) and convert an RTF (not avaible with the
    > Parser that we are using) hampered our ability to solve the following
    > problem. Consider the following set of Data:
    >
    > <example>
    > <Credentials time="3">
    > <UserId>kiss</UserId>
    > </Credentials>
    > <Credentials time="1" break="true">
    > <UserId>bob</UserId>
    > </Credentials>
    > <Credentials time="6" break="true">
    > <UserId>my</UserId>
    > </Credentials>
    > <Credentials time="0">
    > <UserId>Look,</UserId>
    > </Credentials>
    > <Credentials time="2">
    > <UserId>can</UserId>
    > </Credentials>
    > <Credentials time="9">
    > <UserId>tookish</UserId>
    > </Credentials>
    > </example>
    >
    > When sorted in ascending order, the data reads: "Look, bob can kiss my
    > tookish". For our requirements I am processing it as a descending set:
    > "tookish my kiss can bob Look,"
    >
    > That is the first requirement. However, in the even that a node with
    > the property of "break" equal to "true" is found, processing should
    > halt (multiple nodes can have the break property, but we really only
    > care about the first one).
    > So the output should read: "tookish my"
    >
    > I devised a solution by using the substring-before operator to select
    > the first break node's sorted position.
    >
    > <xsl:param name="breakPos">
    > <xsl:for-each select="example/Credentials">
    > <xsl:sort select="@time" order="descending" />
    > <xsl:call-template name="countSequence">
    > </xsl:call-template>
    > </xsl:for-each>
    > </xsl:param>
    > <xsl:param name="endElem">
    > <xsl:value-of select="substring-before($breakPos,'|')" />
    > </xsl:param>
    >
    > <xsl:template match="/">
    > <html>
    > <body>
    > <xsl:for-each select="example/Credentials">
    > <xsl:sort select="@time" order="descending" />
    > <xsl:if test="($endElem = '') or (position() &lt;= $endElem)">
    > <xsl:value-of select="UserId" /><br />
    > </xsl:if>
    > </xsl:for-each>
    > </body>
    > </html>
    > </xsl:template>
    >
    > Yeah, this renders the html output:
    > tookish<br />
    > my<br />
    >
    > but I felt like the "endElem" parameter derivation was kind of a hack.
    > Would there be a better solution- possibly using Meunchian grouping to
    > perform this fix?
    IcedDante, Dec 10, 2006
    #2
    1. Advertising

  3. IcedDante

    Guest

    IcedDante wrote:
    > Working with a sorted group, the inability to use
    > following-sibling (which uses Document Order) and convert
    > an RTF (not avaible with the Parser that we are using)


    Mentioning what processor you're using would've been a good
    idea, instead of mentioning just some of its limitations.

    > hampered our ability to solve the following
    > problem. Consider the following set of Data:


    [XML]

    > When sorted in ascending order, the data reads: "Look,
    > bob can kiss my tookish". For our requirements I am
    > processing it as a descending set: "tookish my kiss can
    > bob Look,"
    >
    > That is the first requirement. However, in the even that
    > a node with the property of "break" equal to "true" is
    > found, processing should halt (multiple nodes can have
    > the break property, but we really only care about the
    > first one). So the output should read: "tookish my"


    [partial solution reeking of imperative programming]

    It would've been a better idea to post the entire
    transformation instead of just parts of it.

    > Yeah, this renders the html output:
    > tookish<br />
    > my<br />


    > but I felt like the "endElem" parameter derivation was
    > kind of a hack. Would there be a better solution-
    > possibly using Meunchian grouping to perform this fix?


    I've grown accustomed to XSLT 2.0, so I can't think of any
    elegant 1.0 solution off the top of my head. The following
    works:

    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
    <result>
    <xsl:apply-templates select="example/Credentials">
    <xsl:sort select="@time" order="descending"/>
    </xsl:apply-templates>
    </result>
    </xsl:template>
    <xsl:template match="Credentials"/>
    <xsl:template
    match=
    "
    Credentials
    [
    not
    (
    ../Credentials
    [@time>current()/@time][@break='true']
    )
    ]
    ">
    <xsl:value-of select="UserId"/><br/>
    </xsl:template>
    </xsl:stylesheet>

    ....but specifying the sorting order in two separate places
    in two different formats is a bit ugly, too, of course.

    With XSLT 2.0, a much more elegant solution is possible:

    <xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()" mode="copy">
    <xsl:copy-of select="."/>
    </xsl:template>
    <xsl:template match="/">
    <xsl:variable name="sorted">
    <xsl:apply-templates
    select="example/Credentials" mode="copy">
    <xsl:sort select="@time" order="descending"/>
    </xsl:apply-templates>
    </xsl:variable>
    <xsl:apply-templates select="$sorted/Credentials"/>
    </xsl:template>
    <xsl:template match="Credentials"/>
    <xsl:template
    match=
    "
    Credentials
    [
    not
    (
    preceding-sibling::Credentials[@break='true']
    )
    ]
    ">
    <xsl:value-of select="UserId"/><br/>
    </xsl:template>
    </xsl:stylesheet>

    [Tested with Saxon-8B]

    --
    Pavel Lepin
    , Dec 11, 2006
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    0
    Views:
    1,413
  2. Tjerk Wolterink
    Replies:
    2
    Views:
    1,404
    Dimitre Novatchev
    Aug 24, 2006
  3. Replies:
    2
    Views:
    815
    Martin Honnen
    Mar 17, 2008
  4. Tony Girgenti

    Gridview header sorted column dimmed after selecting it

    Tony Girgenti, Mar 21, 2007, in forum: ASP .Net Datagrid Control
    Replies:
    0
    Views:
    748
    Tony Girgenti
    Mar 21, 2007
  5. Tony Girgenti

    Gridview header sorted column dimmed after selecting it

    Tony Girgenti, Mar 13, 2007, in forum: ASP .Net Web Controls
    Replies:
    0
    Views:
    130
    Tony Girgenti
    Mar 13, 2007
Loading...

Share This Page