Вот одно из решений
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/programs">
<!-- Variable containing concateneated list of all program elements**
<xsl:variable name="allprograms">
<xsl:apply-templates select="program"/>
</xsl:variable>
<!-- Call recursive template to split the list -->
<xsl:call-template name="output-tokens">
<xsl:with-param name="list" select="$allprograms" />
</xsl:call-template>
</xsl:template>
<!-- Template used to concatenate all program elements -->
<xsl:template match="program">
<xsl:value-of select="concat(@id, ';#') "/>
</xsl:template>
<!-- Recursive template to split out list into unique elements -->
<xsl:template name="output-tokens">
<!-- List to split -->
<xsl:param name="list"/>
<!-- List of all currently processed elements -->
<xsl:param name="newlist" select="';#'" />
<!-- Get first variable in list, and also the remaining part of the list -->
<xsl:variable name="first" select="substring-before($list, ';#')"/>
<xsl:variable name="remaining" select="substring-after($list, ';#')"/>
<!-- Check if first variable is not a number, and is not contained in currently processed list -->
<xsl:if test="number($first) != number($first) and not(contains($newlist, concat(';#', $first, ';#')))">
<id>
<xsl:value-of select="$first"/>
</id>
</xsl:if>
<!-- If there are still elements left in the list, call the template recursively -->
<xsl:if test="$remaining">
<xsl:call-template name="output-tokens">
<xsl:with-param name="list" select="$remaining"/>
<xsl:with-param name="newlist" select="concat($newlist, $first, ';#')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Идея состоит в том, чтобы сначала создать одну строку, содержащую все элементы программы, объединенные вместе.Затем вы вызываете рекурсивный шаблон, который получает первый элемент из списка, а затем проверяет, что он еще не был обработан (это достигается тем, что шаблон также содержит переменную уже обработанных элементов)
Когда XSLTПрименительно к вашему образцу XML выводится следующее:
<id>ttt</id>
<id>tpl</id>
<id>fel</id>
<id>hjk</id>
<id>uio</id>
<id>lpk</id>
<id>ple</id>
<id>hklo</id>