XSLT finding distinct combinations

丶灬走出姿态 提交于 2020-01-15 10:52:22

问题


I am beginner to xslt. I want to find all unique combinations for Year, Month, Day, and Hour.

I have following xml file to transform:

<Plans>
    <Plan Name="Plan_1">
        <Book Name="Book_1">
            <Time Name="AAA">
                <Property Name="Year" Value="2001"/>
                <Property Name="Month" Value="01"/>
                <Property Name="Day" Value="10"/>
                <Property Name="Hour" Value="12"/>
            </Time>
            <Time Name="BBB">
                <Property Name="Year" Value="2001"/>
                <Property Name="Month" Value="01"/>
                <Property Name="Day" Value="10"/>
                <Property Name="Hour" Value="12"/>
            </Time>
            <Time Name="CCC">
                <Property Name="Year" Value="2004"/>
                <Property Name="Month" Value="02"/>
                <Property Name="Day" Value="11"/>
                <Property Name="Hour" Value="04"/>
            </Time>
            <Time Name="DDD">
                <Property Name="Year" Value="2004"/>
                <Property Name="Month" Value="03"/>
                <Property Name="Day" Value="20"/>
                <Property Name="Hour" Value="04"/>
            </Time>
        </Book>
        <Book Name="Book_22">
            <Time Name="CCC">
                <Property Name="Year" Value="2001"/>
                <Property Name="Month" Value="01"/>
                <Property Name="Day" Value="10"/>
                <Property Name="Hour" Value="12"/>
            </Time>
            <Time Name="DDD">
                <Property Name="Year" Value="2002"/>
                <Property Name="Month" Value="03"/>
                <Property Name="Day" Value="23"/>
                <Property Name="Hour" Value="03"/>
            </Time>
            <Time Name="EEE">
                <Property Name="Year" Value="2002"/>
                <Property Name="Month" Value="03"/>
                <Property Name="Day" Value="23"/>
                <Property Name="Hour" Value="03"/>
            </Time>
            <Time Name="FFF">
                <Property Name="Year" Value="2004"/>
                <Property Name="Month" Value="02"/>
                <Property Name="Day" Value="11"/>
                <Property Name="Hour" Value="04"/>
            </Time>
        </Book>
    </Plan>
</Plans>

Input xml has total eight total combinations (Four from each book).

I just want to know distinct combinations. So output should have 6 combinations because there are two same combinations. The name of books does not matter. I just want to see how many combinations there are. And just label from 1 to the number... It does not have to be in any order although it can be ordered by time... If it is too hard to label numbers, then I just need to get distinct combinations only...

<Times>
    <Time Value="1">
        <Property Name="Year" Value="2001"/>
        <Property Name="Month" Value="01"/>
        <Property Name="Day" Value="10"/>
        <Property Name="Hour" Value="12"/>
    </Time>
    <Time Value="2">
        <Property Name="Year" Value="2004"/>
        <Property Name="Month" Value="02"/>
        <Property Name="Day" Value="11"/>
        <Property Name="Hour" Value="04"/>
    </Time>
    <Time Value="3">
        <Property Name="Year" Value="2004"/>
        <Property Name="Month" Value="03"/>
        <Property Name="Day" Value="20"/>
        <Property Name="Hour" Value="04"/>
    </Time>
    <Time Value="4">
        <Property Name="Year" Value="2001"/>
        <Property Name="Month" Value="01"/>
        <Property Name="Day" Value="10"/>
        <Property Name="Hour" Value="12"/>
    </Time>
    <Time Value="5">
        <Property Name="Year" Value="2002"/>
        <Property Name="Month" Value="03"/>
        <Property Name="Day" Value="23"/>
        <Property Name="Hour" Value="03"/>
    </Time>
</Times>

I tried to read for-each-group. But I cannot make it work... Does it have to be four loops or something since there are four porperties? Please help me...


回答1:


Similar to the other answers, you could also make use of xsl:for-each-group here.

<xsl:for-each-group select=".//Time" group-by="string-join(Property/@Value, '|')">

If your XSLT was using the Identity Template, you could then output the distinct Time elements within this group simply by doing this

 <Time Value="{position()}">
     <xsl:apply-templates />
 </Time>

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="/*">
    <Times>
      <xsl:for-each-group select=".//Time" group-by="string-join(Property/@Value, '|')">
          <Time Value="{position()}">
              <xsl:apply-templates />
          </Time>
      </xsl:for-each-group>
    </Times>
  </xsl:template>

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

Now, you mention in comments (but not in the question) about having multiple Plan elements, but you have not said if you want to select distinct Time elements across all Plan elements, or have distinct elements for each separate Plan.

Suppose you did want to show distinct Time elements for each separate Plan, then just a small tweak will do. You would just effectively match the Plan element and have the grouping in that

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />

  <xsl:template match="Plan">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:for-each-group select=".//Time" group-by="string-join(Property/@Value, '|')">
          <Time Value="{position()}">
              <xsl:apply-templates />
          </Time>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

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

Do note these assume the order of your Property elements will always be the same ("Year", "Month", "Day", "Hour"). If not, you can get around this by doing the following

<xsl:for-each-group select=".//Time" group-by="concat(
     Property[@Name='Year']/@Value, '|', 
     Property[@Name='Month']/@Value, '|',
     Property[@Name='Day']/@Value, '|',
     Property[@Name='Hour']/@Value, '|')">



回答2:


Use

distinct-values(/Times/Time/string-join(Property/@Value, '|'))

to get the distinct values. Then if necessary split them up into their component fields using tokenize().




回答3:


The following stylesheet builds upon the answer given by @Michael Kay. This is just to illustrate the solution and prove that it is working. Please accept his answer!

Numbering output elements is not hard, the easiest way is using position() in an attribute value template.

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/Plans">
      <Times>
         <xsl:for-each select="distinct-values(//Time/string-join(Property/@Value, '|'))">
            <xsl:variable name="tokens" select="tokenize(.,'\|')"/>
            <Time Value="{position()}">
               <Property Name="Year" Value="{$tokens[1]}"/>
               <Property Name="Month" Value="{$tokens[2]}"/>
               <Property Name="Day" Value="{$tokens[3]}"/>
               <Property Name="Hour" Value="{$tokens[4]}"/>
            </Time>
         </xsl:for-each>
      </Times>
   </xsl:template>

</xsl:stylesheet>

Output

As mentioned by @Tim C, you are probably expecting 4 result elements.

<?xml version="1.0" encoding="UTF-8"?>
<Times>
   <Time Value="1">
      <Property Name="Year" Value="2001"/>
      <Property Name="Month" Value="01"/>
      <Property Name="Day" Value="10"/>
      <Property Name="Hour" Value="12"/>
   </Time>
   <Time Value="2">
      <Property Name="Year" Value="2004"/>
      <Property Name="Month" Value="02"/>
      <Property Name="Day" Value="11"/>
      <Property Name="Hour" Value="04"/>
   </Time>
   <Time Value="3">
      <Property Name="Year" Value="2004"/>
      <Property Name="Month" Value="03"/>
      <Property Name="Day" Value="20"/>
      <Property Name="Hour" Value="04"/>
   </Time>
   <Time Value="4">
      <Property Name="Year" Value="2002"/>
      <Property Name="Month" Value="03"/>
      <Property Name="Day" Value="23"/>
      <Property Name="Hour" Value="03"/>
   </Time>
</Times>


来源:https://stackoverflow.com/questions/21271925/xslt-finding-distinct-combinations

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!