Now I can loop over the "keywordsArticleNodes" just fine, however, there are double values in them, as an article could be linked to multiple keywords that are the same multiple keywords as on the original article.
So, how do I make a unique collection out of this?
Isn't it better to use a simple XSLT Extension for this. then you can create a list and easily check if an item in the comma seperated list already is added to that list. Was looking if I still had some XSLT Extensions I used in the past to achieve the same goal, but couldn't find it sorry.
Here's my version of it - as you'd expect it's all XSLT (partly due to the fact that I'm a lousy .NET hack at best :-)
And for the record: Benjamin's solution(s) are cool #h5yr
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:umb="urn:umbraco.library"
xmlns:make="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="umb make"
>
<xsl:param name="currentPage" />
<xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::node[@level = 1]" />
<xsl:template match="/">
<!-- Tokenize the keyword ids (could use exslt:tokenize() too) -->
<xsl:variable name="keywords" select="umb:Split($currentPage/data[@alias = 'keywordsPicker'], ',')" />
<!-- Create a variable with a copy of all nodes that have one of these keywords -->
<xsl:variable name="nodes">
<xsl:for-each select="$keywords/value">
<xsl:copy-of select="$siteRoot//node[@nodeTypeAlias = 'Article'][contains(concat(',', data[@alias = 'keywordsPicker'], ','), concat(',', current(), ','))]" />
</xsl:for-each>
</xsl:variable>
<xsl:variable name="nodeset" select="make:node-set($nodes)" /><!-- Create a nodeset from those -->
<!-- Do stuff with nodes in the set that does not have a preceding sibling with that same id -->
<xsl:apply-templates select="$nodeset/node[not(@id = preceding-sibling::node/@id)]" />
</xsl:template>
<!-- Template for Article items -->
<xsl:template match="node[@nodeTypeAlias = 'Article']">
<!-- What ever you need to do with them... -->
</xsl:template>
</xsl:stylesheet>
Notes:
- Uses the same technique Christian illustrated (if set:distinct() worked in the EXSLT library I'd probably go with that)
- No need to call GetXmlNodeById() (You do that for apparently no reason, since you're only using the id, which you already have :-)
- The contains() expression gets clumsy really fast because we need to safeguard against getting matches for a node with id="123" when keyword is "12" or "23"
Awesomeness Chriztian! Wish I could change the solution vote to your solution, it's elegant and doesn't need an extra xslt extension.
I really dig the umb and make shortcuts, going to use those more, handy!
The GetXmlNodeById() was indeed an artifact left behind after some refactors.
Currently the contains() is perfect, right? Although, I'm not sure if I'm reading this correctly.. Say we have a string:
1242,1249,1240
Then you try you contains() method. I see you're adding a comma before and after, so wouldn't asking for1242 and 1240 fail? Because they don't start or end with a comma?
Yes, we put a comma on both ends AND (the crucial part) we put a comma on both ends of the one we're looking for too, so we're actually looking for ",1240," or ",1242," inside the ",1242,1249,1240," string... makes sense? (It's a common trick and Benjamin's doin' it too, I can see :-)
The "make" prefix has a cool benefit that will probably make more sense when I get around to finishing Part III of my "Entity Tricks Trilogy" :-)
Turning a set of nodes (with doubles) into a unique collection
So I have a bit of a challange:
I have nodes that have a treeMultiPicker on them (stores the picked nodes comma seperated: 12,28,22). The picked nodes are for tagging an article.
When I go to that article I need a list of other articles that are tagged with at least ONE of the current article's tags.
What I've got so far:
Now I can loop over the "keywordsArticleNodes" just fine, however, there are double values in them, as an article could be linked to multiple keywords that are the same multiple keywords as on the original article.
So, how do I make a unique collection out of this?
I'd write some c# to do it, stick it into a class in a DLL, and call it much like you do with umbraco.library.
RemoveDuplicates() function..
Hi Sebastiaan,
Isn't it better to use a simple XSLT Extension for this. then you can create a list and easily check if an item in the comma seperated list already is added to that list. Was looking if I still had some XSLT Extensions I used in the past to achieve the same goal, but couldn't find it sorry.
Cheers,
Richard
Hi Richard/Sebastiaan,
Suggested this codeblock inside an XSLT extension (love LINQ):
HTH,
Benjamin
EDIT: I thought of an extra bit that makes it useful in XSLT:
IIRC umbraco.library.Split spits out elements around each item wrapped inside an XPathNodeIterator which gives it purpose in XSLT.
Awesome one-liner Benjamin!
Old skool method:
Cheers, Lee.
It's also a shame that the "ExsltSets:distinct()" doesn't work correctly. (Maybe I shouldn't complain and write a patch? ;-P)
I'm using this to filter for distinct nodes in a home-brewed nodeset:
<xsl:for-each select="$yournodeset/somenode[not(@name = preceding-sibling::somenode/@name)]">
...
</xsl:for-each>
Thanks guys for great answers!
Went with Ben's 2nd approach, which returns a nice XPathNodeIterator, so I can easily use it in XSLT again without splitting, yay!
Lee: Yes please.. ;-)
Christian: Interesting approach, not sure I understand it yet, need to study it a bit closer!
Hi Sebastiaan,
Here's my version of it - as you'd expect it's all XSLT (partly due to the fact that I'm a lousy .NET hack at best :-)
And for the record: Benjamin's solution(s) are cool #h5yr
Notes:
- Uses the same technique Christian illustrated (if set:distinct() worked in the EXSLT library I'd probably go with that)
- No need to call GetXmlNodeById() (You do that for apparently no reason, since you're only using the id, which you already have :-)
- The contains() expression gets clumsy really fast because we need to safeguard against getting matches for a node with id="123" when keyword is "12" or "23"
Cheers,
Chriztian
Awesomeness Chriztian! Wish I could change the solution vote to your solution, it's elegant and doesn't need an extra xslt extension.
I really dig the umb and make shortcuts, going to use those more, handy!
The GetXmlNodeById() was indeed an artifact left behind after some refactors.
Currently the contains() is perfect, right? Although, I'm not sure if I'm reading this correctly.. Say we have a string:
1242,1249,1240
Then you try you contains() method. I see you're adding a comma before and after, so wouldn't asking for1242 and 1240 fail? Because they don't start or end with a comma?
Thrilled you like it :-)
What's going on with that contains(), huh?
Yes, we put a comma on both ends AND (the crucial part) we put a comma on both ends of the one we're looking for too, so we're actually looking for ",1240," or ",1242," inside the ",1242,1249,1240," string... makes sense? (It's a common trick and Benjamin's doin' it too, I can see :-)
The "make" prefix has a cool benefit that will probably make more sense when I get around to finishing Part III of my "Entity Tricks Trilogy" :-)
/Chriztian
Obvious!! I'm tired and missed that one. Excellent little trick indeed! Thanks again!
Hi!
Im trying to use this with Gareth Evans Sniper Systems Tags Control package. Ive adjusted the xsl to the new schema but I get duplicate nodes.
For each tag the same node is inserted in the nodeset so I assume ive done some mistake in the conversion to the new schema?
is working on a reply...