[xsl] Re: Selecting Random elements

Subject: [xsl] Re: Selecting Random elements
From: "Dimitre Novatchev" <dnovatchev@xxxxxxxxx>
Date: Thu, 6 Nov 2003 22:32:22 +0100
> Rather than selecting all elements in a given context, is it possible to
> select a random set of a predifined number. For example given the
> following XML file which could contain hundreds of images, is it
> possible to output a randomly selected set of 3
>
> <imglibrary>
> <img src="1.jpg"/>
> <img src="2.jpg"/>
> <img src="3.jpg"/>
> etc...
> </imglibrary>
>
> The output could be something like:
>
> <img src="53"/>
> <img src="74"/>
> <img src="81"/>

One can use the random.xsl module of FXSL as a base and also follow the
approach taken in the randomList.xsl module.

The result is the following transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
 xmlns:ext="http://exslt.org/common";
 exclude-result-prefixes="ext"
 >
 <xsl:import href="random.xsl"/>

 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:param name="pSeed" select="17489"/>

  <xsl:param name="pNodes" select="/*/*"/>
  <xsl:param name="pnumRandoms" select="10"/>

  <xsl:variable name="vnumNodes" select="count($pNodes)"/>

  <xsl:template match="/">

    <xsl:if test="$pnumRandoms > $vnumNodes">
      <xsl:message terminate="yes">
         Number of desired random nodes exceeds total number of nodes.
      </xsl:message>
    </xsl:if>

    <xsl:variable name="vRandSequence">
      <xsl:call-template name="randomSequence">
        <xsl:with-param name="pLength" select="$pnumRandoms"/>
        <xsl:with-param name="pSeed" select="$pSeed"/>
      </xsl:call-template>
    </xsl:variable>

    <xsl:variable name="vrandInds">
      <xsl:call-template name="_vScale">
        <xsl:with-param name="pRandSeq"
             select="ext:node-set($vRandSequence)/*"/>
        <xsl:with-param name="pstartIndex"
                        select="$vnumNodes - $pnumRandoms"/>
        <xsl:with-param name="pmaxIndex" select="$vnumNodes"/>
      </xsl:call-template>
    </xsl:variable>

    <xsl:call-template name="takeNNodes">
      <xsl:with-param name="pNodes" select="$pNodes"/>
      <xsl:with-param name="pInds" select="ext:node-set($vrandInds)/*"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="takeNNodes">
    <xsl:param name="pNodes" select="/.."/>
    <xsl:param name="pInds" select="/.."/>

    <xsl:if test="$pInds">
      <xsl:copy-of select="$pNodes[position() = $pInds[1]]"/>

      <xsl:call-template name="takeNNodes">
        <xsl:with-param name="pNodes"
             select="$pNodes[position() != $pInds[1]]"/>
        <xsl:with-param name="pInds"
             select="$pInds[position() > 1]"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template name="_vScale">
    <xsl:param name="pRandSeq" select="/.."/>
    <xsl:param name="pstartIndex" select="1"/>
    <xsl:param name="pmaxIndex" select="1"/>

    <xsl:if test="$pRandSeq
                and
                  not($pmaxIndex &lt; $pstartIndex) ">
      <el>
        <xsl:call-template name="_randScale">
          <xsl:with-param name="arg2" select="1"/> <!--start-->
          <xsl:with-param name="arg3"
                      select="$pmaxIndex"/> <!--end-->
          <xsl:with-param name="arg1" select="$pRandSeq[1]"/>
        </xsl:call-template>
      </el>

      <xsl:call-template name="_vScale">
         <xsl:with-param name="pRandSeq"
                        select="$pRandSeq[position() > 1]"/>
         <xsl:with-param name="pstartIndex" select="$pstartIndex"/>
         <xsl:with-param name="pmaxIndex" select="$pmaxIndex - 1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template name="_randScale" >
    <xsl:param name="arg2" select="0"/> <!--pStart-->
    <xsl:param name="arg3" select="1"/> <!--pEnd  -->
    <xsl:param name="arg1"/>            <!--pN    -->

    <xsl:value-of
         select="floor( ($arg3 - $arg2 + 1) div ($modulus - 1)
                         * $arg1
                        + $arg2
                       )"/>
  </xsl:template>

</xsl:stylesheet>

When this transformation is applied on the following source.xml:

<imglibrary>
  <img src="1.jpg"/>
  <img src="2.jpg"/>
  <img src="3.jpg"/>
  <img src="4.jpg"/>
  <img src="5.jpg"/>
  <img src="6.jpg"/>
  <img src="7.jpg"/>
  <img src="8.jpg"/>
  <img src="9.jpg"/>
  <img src="10.jpg"/>
  <img src="11.jpg"/>
  <img src="12.jpg"/>
  <img src="13.jpg"/>
  <img src="14.jpg"/>
  <img src="15.jpg"/>
  <img src="16.jpg"/>
  <img src="17.jpg"/>
  <img src="18.jpg"/>
  <img src="19.jpg"/>
  <img src="20.jpg"/>
  <img src="21.jpg"/>
  <img src="22.jpg"/>
  <img src="23.jpg"/>
  <img src="24.jpg"/>
  <img src="25.jpg"/>
  <img src="26.jpg"/>
  <img src="27.jpg"/>
  <img src="28.jpg"/>
  <img src="29.jpg"/>
  <img src="30.jpg"/>
  <img src="31.jpg"/>
  <img src="32.jpg"/>
  <img src="33.jpg"/>
  <img src="34.jpg"/>
  <img src="35.jpg"/>
  <img src="36.jpg"/>
  <img src="37.jpg"/>
  <img src="38.jpg"/>
  <img src="39.jpg"/>
  <img src="40.jpg"/>
  <img src="41.jpg"/>
  <img src="42.jpg"/>
  <img src="43.jpg"/>
  <img src="44.jpg"/>
  <img src="45.jpg"/>
  <img src="46.jpg"/>
  <img src="47.jpg"/>
  <img src="48.jpg"/>
  <img src="49.jpg"/>
  <img src="50.jpg"/>
  <img src="51.jpg"/>
  <img src="52.jpg"/>
  <img src="53.jpg"/>
  <img src="54.jpg"/>
  <img src="55.jpg"/>
  <img src="56.jpg"/>
  <img src="57.jpg"/>
  <img src="58.jpg"/>
  <img src="59.jpg"/>
  <img src="60.jpg"/>
  <img src="61.jpg"/>
  <img src="62.jpg"/>
  <img src="63.jpg"/>
  <img src="64.jpg"/>
  <img src="65.jpg"/>
  <img src="66.jpg"/>
  <img src="67.jpg"/>
  <img src="68.jpg"/>
  <img src="69.jpg"/>
  <img src="70.jpg"/>
  <img src="71.jpg"/>
  <img src="72.jpg"/>
  <img src="73.jpg"/>
  <img src="74.jpg"/>
  <img src="75.jpg"/>
  <img src="76.jpg"/>
  <img src="77.jpg"/>
  <img src="78.jpg"/>
  <img src="79.jpg"/>
  <img src="80.jpg"/>
  <img src="81.jpg"/>
  <img src="82.jpg"/>
  <img src="83.jpg"/>
  <img src="84.jpg"/>
  <img src="85.jpg"/>
  <img src="86.jpg"/>
  <img src="87.jpg"/>
  <img src="88.jpg"/>
  <img src="89.jpg"/>
  <img src="90.jpg"/>
  <img src="91.jpg"/>
  <img src="92.jpg"/>
  <img src="93.jpg"/>
  <img src="94.jpg"/>
  <img src="95.jpg"/>
  <img src="96.jpg"/>
  <img src="97.jpg"/>
  <img src="98.jpg"/>
  <img src="99.jpg"/>
  <img src="100.jpg"/>
</imglibrary>

the following result is produced:

<img src="27.jpg" />
<img src="91.jpg" />
<img src="14.jpg" />
<img src="80.jpg" />
<img src="67.jpg" />
<img src="13.jpg" />
<img src="30.jpg" />
<img src="92.jpg" />
<img src="81.jpg" />
<img src="37.jpg" />

Here we produce 10 random nodes of the original 100 nodes.

There are three global parameters used:

  <xsl:param name="pSeed" select="17489"/>

  <xsl:param name="pNodes" select="*/*"/>
  <xsl:param name="pnumRandoms" select="10"/>

The "pSeed" parameter may be used as is or can be passed from outside the
transformation (e.g. based on the current time).

The "pNodes" parameter is the node-set from which $pnumRandoms number of
nodes should be taken randomly.


=====
Cheers,

Dimitre Novatchev.
http://fxsl.sourceforge.net/ -- the home of FXSL





 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread