I need to get only unique nodes one by one. Having the following piece I am getting more than a sinlge node every time I use it, if I try to put position() = 1 or [1] at the end then I always get just the first. Any ideas?
<xsl:copy-of select="$currentPage/descendant::*[@isDoc] /* [name() = $documentTypeAlias and @parentID = 1067 and string(nodeShowOnHome) = 1 and not(@id = preceding-sibling::Project/@id)]"/>
I don't fully understand what you're trying to do — could you maybe share a little more info?
The last predicate - "and not(@id = preceding-sibling::Project/@id)" does not make sense to me - ID's are unique, so you can never have a node which has the same @id as another. Are you by any chance looping through a set of nodes, copying specific nodes into a variable? I guess that's where you could run into this...
Another problem is that these axes (preceding::, preceding-sibling:: etc.) *always* work in "document order", so you can't use them to test against the nodes you've previously copied, if that's what you're doing.
Let us know a little more of why you need to copy these nodes, and I'm sure we can help.
If you're able to select all the nodes you need in a single select="...", you don't need to worry about duplicates, because a node can only exist once in a set (using copy-of destroys that), even if you select multiple "almost identical" sets, e.g.:
<!-- Even though the first will include all the nodes in the second, only one of each will exist in the $nodes variable -->
<xsl:variable name="nodes" select="$currentPage//*[@isDoc] | $currentPage/*[@isDoc]" />
That was a completely wrong approach from my site, you're right I was trying to loop and copy specific nodes into specific position of a cusotm xml. The idea is that I got two different node groups one with videos (@parentID=1066) and one with photos (@parentID=1067). I wanted to make a custom xml with videos and in random position add a photo and then again anag again.
Here is the correct approach. Any other ideas are welcome.
Cheers, Giorgos
<xsl:variable name="nodes"> <root> <xsl:for-each select="$currentPage/ancestor::root//* [@isDoc] /* [name() = $documentTypeAlias and @parentID = 1066 and string(nodeShowOnHome) = 1]"> <xsl:copy-of select="."/> <xsl:if test="position() mod $RandomPhoto=0"> <xsl:variable name="index" select="number(position() div $RandomPhoto)"/> <xsl:copy-of select="$currentPage/descendant::*[@isDoc] /* [name() = $documentTypeAlias and @parentID = 1067 and string(nodeShowOnHome) = 1][$index]"/> </xsl:if> </xsl:for-each> </root> </xsl:variable>
Great - that makes sense! Usually when doing something like that, I just create a "pointer" node in the collection variable, which I can then use to find the original node — that way the processor won't have to copy the full subtree etc. — here's my approach to it; feel free to ask away about it:
<!-- Grab the container nodes -->
<xsl:variable name="videoRoot" select="umbraco.library:GetXmlNodeById(1066)" />
<xsl:variable name="photoRoot" select="umbraco.library:GetXmlNodeById(1067)" />
<!-- Grab the relevant children -->
<xsl:variable name="videos" select="$videoRoot/*[name() = $documentTypeAlias][nodeShowOnHome = 1]" />
<xsl:variable name="photos" select="$photoRoot/*[name() = $documentTypeAlias][nodeShowOnHome = 1]" />
<!-- Build a custom set of pointers -->
<xsl:variable name="nodesProxy">
<root>
<xsl:for-each select="$videos">
<!-- Store a reference to the video node -->
<videoRef id="{@id}" />
<xsl:if test="position() mod $RandomPhoto = 0">
<xsl:variable name="index" select="position() div $RandomPhoto" />
<!-- Store a reference to the photo node -->
<photoRef id="{$photos[$index]/@id}" />
</xsl:if>
</xsl:for-each>
</root>
</xsl:variable>
<xsl:variable name="nodes" select="msxml:node-set($nodesProxy)/root/*" />
<xsl:template match="/">
<div>
<!-- Process the videoRef + photoRef nodes -->
<xsl:apply-templates select="$nodes" />
</div>
</xsl:template>
<xsl:template match="videoRef | photoRef">
<!-- Find the real node (it must be in the combined set of videos and photos, since that is where we took it from) -->
<xsl:variable name="node" select="($videos | $photos)[@id = current()/@id]" />
<p>
<!-- Output stuff from the node -->
<xsl:value-of select="$node/@nodeName" />
</p>
</xsl:template>
Just doing the "container" variables will greatly improve the performance of the lookups, because you're no longer going up & down the tree in every iteration.
Not copying entire nodes is guaranteed to speed things up — if not, beer's on me :-)
Preceding sibling for unique nodes
Hello,
I need to get only unique nodes one by one. Having the following piece I am getting more than a sinlge node every time I use it, if I try to put position() = 1 or [1] at the end then I always get just the first. Any ideas?
Thanks, Giorgos
Hi Giorgos,
I don't fully understand what you're trying to do — could you maybe share a little more info?
The last predicate - "and not(@id = preceding-sibling::Project/@id)" does not make sense to me - ID's are unique, so you can never have a node which has the same @id as another. Are you by any chance looping through a set of nodes, copying specific nodes into a variable? I guess that's where you could run into this...
Another problem is that these axes (preceding::, preceding-sibling:: etc.) *always* work in "document order", so you can't use them to test against the nodes you've previously copied, if that's what you're doing.
Let us know a little more of why you need to copy these nodes, and I'm sure we can help.
If you're able to select all the nodes you need in a single select="...", you don't need to worry about duplicates, because a node can only exist once in a set (using copy-of destroys that), even if you select multiple "almost identical" sets, e.g.:
/Chriztian
Hi Chriztian,
That was a completely wrong approach from my site, you're right I was trying to loop and copy specific nodes into specific position of a cusotm xml. The idea is that I got two different node groups one with videos (@parentID=1066) and one with photos (@parentID=1067). I wanted to make a custom xml with videos and in random position add a photo and then again anag again.
Here is the correct approach. Any other ideas are welcome.
Cheers, Giorgos
<xsl:variable name="nodes">
<root>
<xsl:for-each select="$currentPage/ancestor::root//* [@isDoc] /* [name() = $documentTypeAlias and @parentID = 1066 and string(nodeShowOnHome) = 1]">
<xsl:copy-of select="."/>
<xsl:if test="position() mod $RandomPhoto=0">
<xsl:variable name="index" select="number(position() div $RandomPhoto)"/>
<xsl:copy-of select="$currentPage/descendant::*[@isDoc] /* [name() = $documentTypeAlias and @parentID = 1067 and string(nodeShowOnHome) = 1][$index]"/>
</xsl:if>
</xsl:for-each>
</root>
</xsl:variable>
Hi Giorgos,
Great - that makes sense! Usually when doing something like that, I just create a "pointer" node in the collection variable, which I can then use to find the original node — that way the processor won't have to copy the full subtree etc. — here's my approach to it; feel free to ask away about it:
/Chriztian
That's great approach, so do you think that can be fatser, right?
Thanks Chritzian!
Oh yes, I think it is :-)
Just doing the "container" variables will greatly improve the performance of the lookups, because you're no longer going up & down the tree in every iteration.
Not copying entire nodes is guaranteed to speed things up — if not, beer's on me :-)
/Chriztian
is working on a reply...