You can use the implicit existential XPath quantification for the = operator:
<xsl:for-each select="//vertex[not(@name = //vertex/directed-edge-to/@vertex)]">
When you use any of the six comparison operators ( = ,! != , < , <= , > And >= ) to compare a node-set, the expression will return true if any node in node -set satisfies the condition. When comparing one node-set with another, the expression returns true if any node in the first node-set satisfies the condition compared to any node in the second node-set. XPath 2.0 introduces six new operators that do not perform this existential quantification ( eq , ne , lt , le , gt and ge ). But in your case, you will want to use " = " to get this existential quantification.
Note that you still want to use the not() function, as you did. Most of the time, it is best to avoid the != Operator. If you used it here instead of not() , then it will return true if there are any @vertex attributes that are not equal to the @name value, which is not your intention. (And if any node-set is empty, then it will return false, since comparing with empty node-sets always returns false.)
If you want to use eq instead, you will need to do something like this: separate the conditional from the iteration so that you can bind current() . But in XPath 2.0 you can do this in an expression:
<xsl:for-each select="for $v in //vertex return $v[not(//directed-edge-to[@vertex eq $v/@name])]">
This is useful when your condition is not a simple equality comparison (and therefore, it cannot be quantified with " = "). For example: starts-with(@vertex, $v/@name) .
XPath 2.0 also has an explicit way to do existential quantification. Instead of the for statement above, we could write this:
<xsl:for-each select="//vertex[not(some $e in //directed-edge-to satisfies @name eq $e/@vertex)]">
In addition to the " some " syntax, XPath 2.0 also provides the corresponding " every " syntax for performing universal quantification.
Instead of using for-each you can also use template rules that are more modular (and powerful):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <minimal-vertices> <xsl:apply-templates/> </minimal-vertices> </xsl:template> <xsl:template match="vertex[not(@name = //directed-edge-to/@vertex)]"> <minimal-vertex name="{@name}"/> </xsl:template> </xsl:stylesheet>
Again, in this case, we rely on an existential quantification = .
XSLT 1.0 prohibits the use of the current() function in templates, i.e. in the match attribute, but XSLT 2.0 allows this. In this case, current() refers to the node that is currently being mapped. So in XSLT 2.0 we could write this (without using the for statement):
<xsl:template match="vertex[not(//directed-edge-to[@vertex eq current()/@name])]">
Please note: this pattern essentially matches the expression you tried to use in for-each , but while it does not do what you want in for-each , it does what you want in the pattern (because current() binds to another).
Finally, I will add another variation that somehow simplifies the logic (removing not() ). This also applies to using XSLT 1.0:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <minimal-vertices> <xsl:apply-templates/> </minimal-vertices> </xsl:template> <xsl:template match="vertex"> <minimal-vertex name="{@name}"/> </xsl:template> <xsl:template match="vertex[@name = //directed-edge-to/@vertex]"/> </xsl:stylesheet>
If you do not like the spaces displayed, add an empty rule for text nodes, so they will be deleted (overriding the default rule for text nodes that should copy them):
<xsl:template match="text()"/>
Or you can simply be more selective in which nodes you apply the templates to:
<xsl:apply-templates select="/dag/vertex"/>
Which approach you take, partly depends on taste, partly depends on the wider context of your stylesheet and the expected data (how much the input structure can be changed, etc.).
I know that I went beyond what you asked for, but I hope you at least find it interesting. :-)