Topics

Randomizing Child Nodes of a Parent


Westley W.
 

Hey everyone,

I have a situation where I have hundreds of DITA files that contain <ul> elements that contain any number of child <li> elements which need to be randomized. As an example:

<ul>
<li>List Item 1</li>
<li>List Item 2</li>
<li>List Item 3</li>
</ul>

Becomes randomized, one possible outcome:

<ul>
<li>List Item 2</li>
<li>List Item 3</li>
<li>List Item 1</li>
</ul>

I was curious what your best approach would be to randomize the order of these <li> elements for each DITA file? I am attempting to achieve this through a Powershell script with some RegEx but am burning myself out. Another thought I had was utilizing XML Refactoring but I'm not perceiving any logic that could work with XSLT or XQuery.  I feel like there should be an easy solution.  

Thanks,

Westley


George Cristian Bina
 

Hello,

If XSLT 3 is an option, then you may use something like the stylesheet below (this can be easily set also as a refactoring action in oXygen, if you use it, the advantage being that it will keep the rest of your document as it is, DOCTYPE declaration, etc.):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">

<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>

<xsl:template match="ul">
<xsl:variable name="random" select="random-number-generator()"/>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:variable name="n" select="count(li)"/>
<xsl:variable name="perm" select="random-number-generator()?permute(1 to $n)"/>
<xsl:message select="$perm"></xsl:message>
<xsl:for-each select="node()">
<xsl:choose>
<xsl:when test="self::li">
<xsl:variable name="index" select="count(preceding-sibling::li) + 1"/>
<xsl:apply-templates select="../li[$perm[$index]]"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Best Regards,
George
--
George Cristian Bina
<oXygen/> XML Editor, Schema Editor and XSLT Editor/Debugger
http://www.oxygenxml.com

On 10/28/20 12:24 AM, Westley W. wrote:
Hey everyone,
I have a situation where I have hundreds of DITA files that contain <ul> elements that contain any number of child <li> elements which need to be randomized. As an example:
<ul>
<li>List Item 1</li>
<li>List Item 2</li>
<li>List Item 3</li>
</ul>
Becomes randomized, one possible outcome:
<ul>
<li>List Item 2</li>
<li>List Item 3</li>
<li>List Item 1</li>
</ul>
I was curious what your best approach would be to randomize the order of these <li> elements for each DITA file? I am attempting to achieve this through a Powershell script with some RegEx but am burning myself out. Another thought I had was utilizing XML Refactoring but I'm not perceiving any logic that could work with XSLT or XQuery.  I feel like there should be an easy solution.
Thanks,
Westley


Westley W.
 

George,

This works wonderful.  I didn't expect anyone to provide a script, I can't thank you enough.  I'm pretty green when it comes to XSLT, however, I was able to implement this as a Refactoring option easily as you said.

I ran into one issue where a DITA file could have multiple <ul> elements with same number of <li> elements which results in the same order generated. A little bit of searching lead me to this StackOverflow post where I modified the code as such:
...
<xsl:variable name="random" select="random-number-generator()"/>
<xsl:variable name="dummy" select="generate-id()"/>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:variable name="n" select="count(li)"/>
<xsl:variable name="perm" select="random-number-generator($dummy)?permute(1 to $n)"/>
....

As the post suggested, it's probably not proper but it resolved the few DITA files that did have this issue. 

Thanks again, George!