在XSLT中对元素进行分组,第一个元素除外

时间:2022-11-02 07:36:28

(the title of this question might not be the correct one, so I will change it if someone has a better suggestion.)

(这个问题的标题可能不正确,所以如果有人有更好的建议,我会改变它。)

I have a list of results from a sport event where I want to have the following output:

我有一个体育赛事的结果列表,我希望得到以下输出:

<p>
1) John Doe, Norway 1.23,45, 2) Dave Doe, Norway 0.15 behind, 3) Nicholas Doe, Norway Same Time, 4) Barack Doe, USA S.T, 5) Vladimir Doe, Russia 1.00,00 behind, 6) Xi Min Doe, China S.T. .... 
</p>

In written words: First results has the total time. Second results uses gap and we add behind, if the third result has the same gap as the second result we give that one same time. If the fourth result has the same gap as second result, we shorten it to s.t. If there is a new gap, we write out the gap, but all results who has the same gap as the first one in this group, shall only have s.t.

用文字说:第一个结果有总时间。第二个结果使用间隙,我们添加后面,如果第三个结果与第二个结果具有相同的间隙,我们给出同一时间。如果第四个结果与第二个结果具有相同的差距,我们将其缩短为s.t.如果存在新的差距,我们会写出差距,但是与该组中第一个差距相同的所有结果只有s.t.

Alternative output:

<results>
<result>
<rank>1</rank>
<name>John</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:23:45</time>
<gap>00:00</gap>
</result>

<result>
<rank>2</rank>
<name>Dave</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:00</time>
<gap>00:15</gap>
</result>

<result>
<rank>3</rank>
<name>Nicholas</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:00</time>
<gap>same time</gap>
</result>

<result>
<rank>4</rank>
<name>Barack</name>
<lastname>Doe</name>
<country>USA</country>
<time>1:24:00</time>
<gap>s.t.</gap>
</result>

<result>
<rank>5</rank>
<name>Vladimir</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:45</time>
<gap>01:15</gap>
</result>

<result>
<rank>6</rank>
<name>Xi Min</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:45</time>
<gap>s.t.</gap>
</result>
</results>

the XML is quite similar to this:

XML非常类似于:

<results>
<result>
<rank>1</rank>
<name>John</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:23:45</time>
<gap>00:00</gap>
</result>

<result>
<rank>2</rank>
<name>Dave</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:00</time>
<gap>00:15</gap>
</result>

<result>
<rank>3</rank>
<name>Nicholas</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:00</time>
<gap>00:15</gap>
</result>

<result>
<rank>4</rank>
<name>Barack</name>
<lastname>Doe</name>
<country>USA</country>
<time>1:24:00</time>
<gap>00:15</gap>
</result>

<result>
<rank>5</rank>
<name>Vladimir</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:45</time>
<gap>01:15</gap>
</result>

<result>
<rank>6</rank>
<name>Xi Min</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:45</time>
<gap>01:15</gap>
</result>
</results>

The output, as code: I am using XSLT 2.0, but there might be someone out there who are using 1.0, so any suggestions supporting either (or make that all) versions of XSLT, I and many with me in the same situation would be very happy.

输出,作为代码:我正在使用XSLT 2.0,但可能有人在那里使用1.0,所以任何建议支持XSLT的任何(或使所有)版本,我和许多人在同样的情况下将是很高兴。

2 个解决方案

#1


0  

The following stylesheet:

以下样式表:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="/results">
    <xsl:copy>
        <xsl:for-each-group select="result" group-by="gap">
            <xsl:apply-templates select="current-group()"/>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<xsl:template match="result">
    <xsl:copy>
        <xsl:apply-templates select="* except gap"/>
        <gap>
            <xsl:value-of select="if (position()=1) then gap else 'SAME TIME'" />
        </gap>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

when applied to your example input (after correcting the mismatching name and lastname tags!), will return:

当应用于您的示例输入时(在更正不匹配的名称和姓氏标签之后!),将返回:

<?xml version="1.0" encoding="UTF-8"?>
<results>
   <result>
      <rank>1</rank>
      <name>John</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:23:45</time>
      <gap>00:00</gap>
   </result>
   <result>
      <rank>2</rank>
      <name>Dave</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:00</time>
      <gap>00:15</gap>
   </result>
   <result>
      <rank>3</rank>
      <name>Nicholas</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:00</time>
      <gap>SAME TIME</gap>
   </result>
   <result>
      <rank>4</rank>
      <name>Barack</name>
      <lastname>Doe</lastname>
      <country>USA</country>
      <time>1:24:00</time>
      <gap>SAME TIME</gap>
   </result>
   <result>
      <rank>5</rank>
      <name>Vladimir</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:45</time>
      <gap>01:15</gap>
   </result>
   <result>
      <rank>6</rank>
      <name>Xi Min</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:45</time>
      <gap>SAME TIME</gap>
   </result>
</results>

#2


0  

This was my solution to the problem. I was told after the post that only top 10 shall be rendered and so I have added a if rank <= 10 around the code.

这是我解决问题的方法。在帖子之后我被告知只有前10名才会被渲染,所以我在代码周围添加了一个if = <10。

Some part of the original data source structure here (just one to show data, names and Ids has been removed).

这里是原始数据源结构的一部分(只有一个显示数据,名称和ID已被删除)。

If you find something odd, please do not hesitate to comment or ask question.

如果您发现奇怪的内容,请随时发表评论或提出问题。

<RESULT>
        <rank>42</rank>
        <bib>43</bib>
        <name>John</name>
        <surname>Doe</surname>
        <short_team>ABA</short_team>
        <long_team>ABBA</long_team>
        <time>04:42:18</time>
        <gap>13</gap>
        <code>ABA1234</code>
        <nationality>BEL</nationality>
    </RESULT>

The part of the XSLT to group and get the right output. Mind, this is production code, and so some text may differ from the example. I have added comments where there are differences:

XSLT的一部分,用于分组并获得正确的输出。请注意,这是生产代码,因此某些文本可能与示例不同。我添加了有差异的评论:

<xsl:for-each-group select="RESULT" group-by="time">
            <xsl:for-each select="current-group()">
                <xsl:if test="rank &lt;= 10">
                    <xsl:value-of select="rank"/>
                    <xsl:text>) </xsl:text>
                    <xsl:value-of select="name"/>
                    <xsl:text> </xsl:text>
                    <xsl:variable name="surname">
                        <xsl:for-each select="tokenize(surname, ' ')">
                            <xsl:value-of
                                select="concat(upper-case(substring(., 1, 1)), lower-case(substring(., 2)))"/>
                            <xsl:text> </xsl:text>
                        </xsl:for-each>
                    </xsl:variable>
                    <xsl:value-of select="normalize-space($surname)"/>

                    <xsl:text>, </xsl:text>
                    <xsl:value-of select="nationality"/>
                    <xsl:text> (</xsl:text>
                    <xsl:value-of select="long_team"/>
                    <xsl:text>)</xsl:text>
                    <xsl:text> </xsl:text>
                    <xsl:choose>
                        <xsl:when test="rank = '1'">
                            <!-- the replace is just there to get the time in the correct format -->
                            <xsl:value-of select="replace(time, ':', '.')"/>
                        </xsl:when>
                        <xsl:when test="rank = '2'">
                            <xsl:choose>
                                <xsl:when test="gap != ''">
                                    <xsl:if test="string-length(gap) = 2">
                                       <!-- this text is here because the gap might just have two characters, and we want it like this 0.12 -->
                                        <xsl:text>0.</xsl:text>
                                    </xsl:if>
                                    <xsl:value-of select="replace(gap, ':', '.')"/>
                                    <!-- This is norwegian text for minutes behind (shortened) -->
                                    <xsl:text> min. bak</xsl:text>
                                </xsl:when>
                                <xsl:otherwise>
                                    <!-- This is norwegian for same time -->
                                    <xsl:text> samme tid</xsl:text>
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:choose>
                                <xsl:when test="position() = 1">
                                    <xsl:if test="string-length(gap) = 2">
                                        <xsl:text>0.</xsl:text>
                                    </xsl:if>
                                    <xsl:value-of select="replace(gap, ':', '.')"/>
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:text> s.t.</xsl:text>
                                </xsl:otherwise>
                            </xsl:choose>

                        </xsl:otherwise>
                    </xsl:choose>
                    <xsl:choose>
                        <xsl:when test="position() != last()">
                            <xsl:text>, </xsl:text>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:text>, </xsl:text>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:if>
            </xsl:for-each>
        </xsl:for-each-group>

#1


0  

The following stylesheet:

以下样式表:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="/results">
    <xsl:copy>
        <xsl:for-each-group select="result" group-by="gap">
            <xsl:apply-templates select="current-group()"/>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<xsl:template match="result">
    <xsl:copy>
        <xsl:apply-templates select="* except gap"/>
        <gap>
            <xsl:value-of select="if (position()=1) then gap else 'SAME TIME'" />
        </gap>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

when applied to your example input (after correcting the mismatching name and lastname tags!), will return:

当应用于您的示例输入时(在更正不匹配的名称和姓氏标签之后!),将返回:

<?xml version="1.0" encoding="UTF-8"?>
<results>
   <result>
      <rank>1</rank>
      <name>John</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:23:45</time>
      <gap>00:00</gap>
   </result>
   <result>
      <rank>2</rank>
      <name>Dave</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:00</time>
      <gap>00:15</gap>
   </result>
   <result>
      <rank>3</rank>
      <name>Nicholas</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:00</time>
      <gap>SAME TIME</gap>
   </result>
   <result>
      <rank>4</rank>
      <name>Barack</name>
      <lastname>Doe</lastname>
      <country>USA</country>
      <time>1:24:00</time>
      <gap>SAME TIME</gap>
   </result>
   <result>
      <rank>5</rank>
      <name>Vladimir</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:45</time>
      <gap>01:15</gap>
   </result>
   <result>
      <rank>6</rank>
      <name>Xi Min</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:45</time>
      <gap>SAME TIME</gap>
   </result>
</results>

#2


0  

This was my solution to the problem. I was told after the post that only top 10 shall be rendered and so I have added a if rank <= 10 around the code.

这是我解决问题的方法。在帖子之后我被告知只有前10名才会被渲染,所以我在代码周围添加了一个if = <10。

Some part of the original data source structure here (just one to show data, names and Ids has been removed).

这里是原始数据源结构的一部分(只有一个显示数据,名称和ID已被删除)。

If you find something odd, please do not hesitate to comment or ask question.

如果您发现奇怪的内容,请随时发表评论或提出问题。

<RESULT>
        <rank>42</rank>
        <bib>43</bib>
        <name>John</name>
        <surname>Doe</surname>
        <short_team>ABA</short_team>
        <long_team>ABBA</long_team>
        <time>04:42:18</time>
        <gap>13</gap>
        <code>ABA1234</code>
        <nationality>BEL</nationality>
    </RESULT>

The part of the XSLT to group and get the right output. Mind, this is production code, and so some text may differ from the example. I have added comments where there are differences:

XSLT的一部分,用于分组并获得正确的输出。请注意,这是生产代码,因此某些文本可能与示例不同。我添加了有差异的评论:

<xsl:for-each-group select="RESULT" group-by="time">
            <xsl:for-each select="current-group()">
                <xsl:if test="rank &lt;= 10">
                    <xsl:value-of select="rank"/>
                    <xsl:text>) </xsl:text>
                    <xsl:value-of select="name"/>
                    <xsl:text> </xsl:text>
                    <xsl:variable name="surname">
                        <xsl:for-each select="tokenize(surname, ' ')">
                            <xsl:value-of
                                select="concat(upper-case(substring(., 1, 1)), lower-case(substring(., 2)))"/>
                            <xsl:text> </xsl:text>
                        </xsl:for-each>
                    </xsl:variable>
                    <xsl:value-of select="normalize-space($surname)"/>

                    <xsl:text>, </xsl:text>
                    <xsl:value-of select="nationality"/>
                    <xsl:text> (</xsl:text>
                    <xsl:value-of select="long_team"/>
                    <xsl:text>)</xsl:text>
                    <xsl:text> </xsl:text>
                    <xsl:choose>
                        <xsl:when test="rank = '1'">
                            <!-- the replace is just there to get the time in the correct format -->
                            <xsl:value-of select="replace(time, ':', '.')"/>
                        </xsl:when>
                        <xsl:when test="rank = '2'">
                            <xsl:choose>
                                <xsl:when test="gap != ''">
                                    <xsl:if test="string-length(gap) = 2">
                                       <!-- this text is here because the gap might just have two characters, and we want it like this 0.12 -->
                                        <xsl:text>0.</xsl:text>
                                    </xsl:if>
                                    <xsl:value-of select="replace(gap, ':', '.')"/>
                                    <!-- This is norwegian text for minutes behind (shortened) -->
                                    <xsl:text> min. bak</xsl:text>
                                </xsl:when>
                                <xsl:otherwise>
                                    <!-- This is norwegian for same time -->
                                    <xsl:text> samme tid</xsl:text>
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:choose>
                                <xsl:when test="position() = 1">
                                    <xsl:if test="string-length(gap) = 2">
                                        <xsl:text>0.</xsl:text>
                                    </xsl:if>
                                    <xsl:value-of select="replace(gap, ':', '.')"/>
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:text> s.t.</xsl:text>
                                </xsl:otherwise>
                            </xsl:choose>

                        </xsl:otherwise>
                    </xsl:choose>
                    <xsl:choose>
                        <xsl:when test="position() != last()">
                            <xsl:text>, </xsl:text>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:text>, </xsl:text>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:if>
            </xsl:for-each>
        </xsl:for-each-group>