'Using muenchian grouping to select distinct values in a subtree

I know how to do muenchian grouping but that normally is applied to a whole document (?).

Here I want to find distinct values in subtrees.

e.g.

<root>
  <parent>
    <child id="1"/>
    <child id="1"/>
    <child id="2"/>
  </parent>
  <parent>
    <child id="2"/>
    <child id="3"/>
    <child id="3"/>
  </parent>
</root>

I want to map to

<root>
  <parent>
    <child id="1"/>
    <child id="2"/>
  </parent>
  <parent>
    <child id="2"/>
    <child id="3"/>
  </parent>
</root>

If I try

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

  <xsl:key name="id_to_id" match="/root/parent/child" use="@id"/>
  
    <xsl:template match="/root">
      <root>
        <xsl:for-each select="parent">
          <parent>
            <xsl:for-each select="child[generate-id() = generate-id(key('id_to_id',@id)[1])]">
              <child id="{@id}"/>
            </xsl:for-each>            
          </parent>       
        </xsl:for-each>
      </root>
    </xsl:template>
</xsl:stylesheet>

then it does the key over the whole document and I get

<root>
  <parent>
    <child id="1" />
    <child id="2" />
  </parent>
  <parent>
    <child id="3" />
  </parent>
</root>


Solution 1:[1]

heres another equally bizarre solution, still not sure this is very sensible, is there some sort of XPath parenthesis or something I can use?

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

  <xsl:key name="id_to_id" match="child" use="@id"/>
  
    <xsl:template match="/root">
      <root>
        <xsl:for-each select="parent">
          <parent>
            <xsl:variable name="children">
              <xsl:copy-of select="child"/>
            </xsl:variable>
            <xsl:for-each select="msxsl:node-set($children)/child[generate-id() = generate-id(key('id_to_id',@id)[1])]">
              <child id="{@id}"/>
            </xsl:for-each>            
          </parent>       
        </xsl:for-each>
      </root>
    </xsl:template>
</xsl:stylesheet>

Solution 2:[2]

now without meunchian grouping (which isnt what I want, but its a nice declarative approach), not suitable for anything but small groups of siblings.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/root">
      <root>
        <xsl:for-each select="parent">
          <parent>
            <xsl:for-each select="child[not(@id = preceding-sibling::child/@id)]">
              <child id="{@id}"/>
            </xsl:for-each>            
          </parent>       
        </xsl:for-each>
      </root>
    </xsl:template>
</xsl:stylesheet>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 MrD at KookerellaLtd
Solution 2