How to select unique nodes

前端 未结 5 928
轮回少年
轮回少年 2020-12-03 01:40

I found this page describing the Muenchian method, but I think I\'m applying it wrong.

Consider that this would return a set of ages:

/doc/class/pers         


        
相关标签:
5条回答
  • 2020-12-03 02:21

    The Muenchian method uses keys to create a unique list of items from the node set. For your data, the key would look like this:

    <!-- Set the name to whatever you want -->
    <xsl:key name="PeopleAges" match="/doc/class/person/descriptive[@name = 'age']/value" use="." />
    

    From there, I would personally use xsl:apply-templates but you can use the following select attribute in other places:

    <!-- you can change `apply-templates` to: `copy-of` or `for-each`. -->
    <xsl:apply-templates select="/doc/class/person/descriptive[@name = 'age']/value[count(. | key('PeopleAges', .)[1]) = 1]" />
    

    The accompanying match for the above is much simpler:

    <xsl:template match="person/descriptive[@name = 'age']/value">
        <strong>Age: </strong><xsl:value-of select="." />
    </xsl:template>
    
    0 讨论(0)
  • 2020-12-03 02:25

    Here's an example:

    <root>
        <item type='test'>A</item>
        <item type='test'>B</item>
        <item type='test'>C</item>
        <item type='test'>A</item>
        <item type='other'>A</item>
        <item type='test'>B</item>
        <item type='other'>D</item>
        <item type=''>A</item>
    </root>
    

    And the XPath:

    //preceding::item/preceding::item[not(.=preceding-sibling::item)]/text()
    

    Results: A B C D

    EDIT: As mousio commented this doesn't capture the last item in a list if it's the only time it appears. Taking that and Fëanor's comment into account, here's a better solution:

    /root/item[not(.=preceding-sibling::item)]
    
    0 讨论(0)
  • 2020-12-03 02:34

    For those who still look for a select distinct in XSLT:

    With XSLT 2.0, you can use "distinct-values(/doc/class/person/descriptive[(@name='age')]/value)"

    0 讨论(0)
  • 2020-12-03 02:38

    Here is the Muenchian version of BQ's answer using his data:

    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
      <xsl:output indent="yes" method="text"/>
      <xsl:key name="item-by-value" match="item" use="."/>
    
      <xsl:template match="/">
        <xsl:apply-templates select="/root/item"/>
      </xsl:template>
    
      <xsl:template match="item">
        <xsl:if test="generate-id() = generate-id(key('item-by-value', normalize-space(.)))">
          <xsl:value-of select="."/>
          <xsl:text>
    </xsl:text>
        </xsl:if>
      </xsl:template>
    
      <xsl:template match="text()">
        <xsl:apply-templates/>
      </xsl:template>
    </xsl:stylesheet>
    

    This transform gives

    A
    B
    C
    D

    1. The key() lookup above in the template for item returns a nodeset containing all the item elements with the same string value as the context node.
    2. If you apply a function that expects a single node to a nodeset, it will operate on the first node in that nodeset.
    3. All calls to generate-id() are guaranteed to generate the same ID for a given node during a single pass through a document.
    4. Therefore, the test will be true if the context node is the same node as the first one returned by the key() call.
    0 讨论(0)
  • 2020-12-03 02:38

    Aren't you missing a reference to 'descriptive' right after the preceding-value? Some thing like the following:

    /doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::descriptive[@name='age']/value)]/value
    

    (Haven't tested it)

    0 讨论(0)
提交回复
热议问题