Getting n random items
The project I’m working on involves showing various case studies. On my case study index page, I wanted to be able to show a selection of random case studies (say, up to four).
I can select all the relevant case studies using:
<xsl:variable name=”featuredall” select=”$sc_item//item[@template=’case study’ and sc:fld(‘featured’,.)!=”]” />
However, selecting a set of random items from this set isn’t as easy as it sounds. Using sc:random won’t work, because there’s a chance that the same case study will be used more than once.
After much head-scratching, I wrote an XSL Extension which returns a simple nodeset of random integers. This allows me to iterate through that nodeset, referring to the relevant position in my set of case study items…
The XSL Extension:
using System; using System.IO; using System.Xml; using System.Xml.XPath; using System.Xml.Xsl; using System.Collections.Generic; using System.Text; namespace Flag08v2.classes { public class RandomTest { public XPathNodeIterator RandomSequence2(int n) { Random randomGen = new Random(); List<int> numbers = new List<int>(n); for (int x = 1; x<=n; x++) numbers.Add(x); List<int> randNumbers = new List<int>(n); for (int x = 0; x<n; x++) { int rn = randomGen.Next(numbers.Count); int rnVal = numbers[rn]; randNumbers.Add(rnVal); numbers.RemoveAt(rn); } Sitecore.Xml.Packet packet = new Sitecore.Xml.Packet("values", new string[0]); foreach (int i in randNumbers) packet.AddElement("value", i.ToString(), new string[0]); XPathNavigator navigator = packet.XmlDocument.CreateNavigator(); navigator.MoveToRoot(); navigator.MoveToFirstChild(); return navigator.SelectChildren(XPathNodeType.Element); } } }
Then, add the reference to the XSL Extension in the <xsl:stylesheet> declaration:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sc="http://www.sitecore.net/sc" xmlns:dot="http://www.sitecore.net/dot" xmlns:mt="http://www.flag.co.uk/random" exclude-result-prefixes="dot sc" >
And to iterate through the case studies (“maxfeatured” is the maximum number of case studies to show – I set this using a field elsewhere):
... <xsl:variable name="featuredall" select="$sc_item//item[@template='case study' and sc:fld('featured',.)!='']" /> <xsl:for-each select="mt:RandomSequence2(count($featuredall))"> <xsl:if test="position() <= $maxfeatured or $maxfeatured = 0"> <xsl:variable name="thisNum" select="text()" /> <xsl:variable name="thisItm" select="$featuredall[position() = $thisNum]" /> Item <xsl:value-of select="text()"/> = <xsl:value-of select="sc:fld('title',$thisItm)"/> </xsl:if> </xsl:for-each>
Hope that makes sense!
Mike