I need an XSLT 1.0 test expression that will indicate whether the elements of the current node t are perfectly interleaved, like this
<t>
<cat />
<dog />
<horse />
<cat />
<dog />
<horse />
</t>
or has some other order, such as
<t>
<cat />
<cat />
<dog />
<dog />
<horse />
<horse />
</t>
or
<t>
<cat />
<dog />
<cat/>
<horse/>
<cat/>
<horse />
</t>
If the first, there can be any number of such tuples. If the second, there can be any number (including zero) of each kind of child and in any order.
The special case of one cat, one dog, one horse can test true or false, whichever makes the algorithm easier.
I do know beforehand the names of the three elements.
EDIT. At Dimitre's request, let me try saying it another, maybe simpler, way.
The context node has any number of children, but each child has one of only three names. Before processing these children, I need to test whether they appear in a repeating pattern, such as A B C A B C A B C, or C A B C A B, or any other combination of repeated triplets, triplets in which each of the three appears once (A B C A B C tests true, A B B A B B tests false).
Provided that the order of the tuple is fixed, this template will return true for all cases where there are 1 or more tuples and false otherwise:
<xsl:template match="t">
<xsl:sequence
select="
count(*) gt 2 and
count(*) = count(*[
self::cat and position() mod 3 = 1 or
self::dog and position() mod 3 = 2 or
self::horse and position() mod 3 = 0])"/>
</xsl:template>
If the order of the tuple can vary, this template will return true for all cases where there are 1 or more tuples that are ordered the same as the first instance of the tuple and false otherwise
<xsl:template match="t">
<xsl:variable name="cat.pos" select="(count(cat[1]/preceding-sibling::*) + 1) mod 3"/>
<xsl:variable name="dog.pos" select="(count(dog[1]/preceding-sibling::*) + 1) mod 3"/>
<xsl:variable name="horse.pos" select="(count(horse[1]/preceding-sibling::*) + 1) mod 3"/>
<xsl:sequence
select="
count(*) gt 2 and
count(*) = count(*[
self::cat and position() mod 3 = $cat.pos or
self::dog and position() mod 3 = $dog.pos or
self::horse and position() mod 3 = $horse.pos])"/>
</xsl:template>
test="name(*[last()])=name(*[3])
and name(*[1])!=name(*[2])
and name(*[2])!=name(*[3])
and name(*[1])!=name(*[3])
and not(*[position() > 3][name()!=name(preceding-sibling::*[3])])"
returns true if the interleave is perfect (or if there are only three items).
Edits: Added the first condition to ensure the final tuple is complete and the three middle conditions to ensure the repeated tuple includes each of the three items (i.e., does not include duplicates).
Related
Follow on question to: https://stackoverflow.com/questions/71635891/how-to-keep-track-of-position-from-a-template-to-a-different-template?noredirect=1#comment126614972_71635891
My new problem is, there could be more than one simplePath in a jump and I want all simplePath inside a jump to start
from the same position. My current is issue is that I'm not competent enough to translate my current idea into XSL, I added in comment in the XSLT code what I'm trying to achieve
D --- E --- F K --- L --- M --- N --- O
/ \ /
A --- B --- C--- / \ -- J ---/
\ / \
\ / \
G --- H --- I Q --- R --- S
<root>
<simplePath>
<point>A</point>
<point>B</point>
<point>C</point>
</simplePath>
<jump>
<simplePath>
<point>D</point>
<point>E</point>
<point>F</point>
</simplePath>
<simplePath>
<point>G</point>
<point>H</point>
<point>I</point>
</simplePath>
</jump>
<simplePath>
<point>J</point>
</simplePath>
</root>
expected output
A : 0
B : 50
C : 100
D : 300
E : 350
F : 400
Dbis : 300
Ebis : 350
Fbis : 400
G : 600
poor attempt :
<xsl:template match="/root">
<xsl:call-template name="process">
<xsl:with-param name="points" select=".//point"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="process">
<xsl:param name="points" />
<xsl:param name="total" select="0"/>
<xsl:param name="savePos" select="0"/>
<!-- new param -->
<!-- output -->
<xsl:value-of select="boolean($points[1]/ancestor::jump)"/>
<xsl:value-of select="$points[1]"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="$total"/>
<!-- recursive call -->
<xsl:if test="count($points) > 1">
<xsl:text>
</xsl:text>
<xsl:call-template name="process">
<xsl:with-param name="points" select="$points[position() > 1]"/>
<xsl:with-param name="total" select="if(boolean($points[1]/ancestor::jump) != boolean($points[2]/ancestor::jump)) then $total + 200 else $total + 50"/>
<!-- my "attempt" -->
<!-- I want to save the pos of total IF it's the first point of the first simplePath in a Jump -->
<xsl:with-param name="savePos" select=""/>
<!-- Next, I will add a new "if condition" here
<xsl:with-param name="total" select="if(boolean($points[1]/ancestor::jump) != boolean($points[2]/ancestor::jump))
then
>>>>if I'm not in the first simplePath of "jump", and it's the first "point" of this "simplePath" then
$total = $savePos
else
$total + 200
else
$total + 50"/>
- -->
</xsl:call-template>
</xsl:if>
</xsl:template>
I have group elements using this expression:
count(. | key('products-by-category', CodiceAttivita)[1]) = 1
Now I need to confront the number of results and say that if is more than 1 show a block of elements.
I think to do something like that
<xsl:if test=" [count(. | key('products-by-category', CodiceAttivita)[1]) = 1] > 1">
But it doesn't work.
How can I fix it?
Thank you
Part of xml is
<Riepilogo>
<IVA>
<AliquotaIVA>4.00</AliquotaIVA>
<Imposta>5830.98</Imposta>
</IVA>
<Ammontare>145879.00</Ammontare>
<ImportoParziale>145774.50</ImportoParziale>
<TotaleAmmontareResi>0.00</TotaleAmmontareResi>
<CodiceAttivita>253000</CodiceAttivita>
</Riepilogo>
<Riepilogo>
<IVA>
<AliquotaIVA>10.00</AliquotaIVA>
<Imposta>645.66</Imposta>
</IVA>
<Ammontare>6587.00</Ammontare>
<ImportoParziale>6456.60</ImportoParziale>
<CodiceAttivita>433100</CodiceAttivita>
</Riepilogo>
<Riepilogo>
<IVA>
<AliquotaIVA>22.00</AliquotaIVA>
<Imposta>618.34</Imposta>
</IVA>
<Ammontare>3254.85</Ammontare>
<ImportoParziale>2810.65</ImportoParziale>
<CodiceAttivita>253000</CodiceAttivita>
</Riepilogo>
What I need is group for CodiceAttivita and define the case when the CodiceAttivita has the same value.
A list of numbers corresponding to the factors is defined with this string : 24850973612485097361
The number is flipped and is multiplied for each number with the factor corresponding to its position, 0 corresponding to 10 and all cumulated
For example, for and order having the number 28200703:
The number is flipped giving 30700282
Using the factor string 24850973612485097361 for the corresponding size for the number, the calculation is done as following:
3 x 2 = 6
0 x 4 = 0
7 x 8 = 56
0 x 5 = 0
0 x 10 = 0
2 x 9 = 18
8 x 7 = 56
2 x 3 = 6
Cumulated: 142
Try it this way:
<xsl:template name="process">
<xsl:param name="number"/>
<xsl:param name="factors" select="'24850973612485097361'"/>
<xsl:param name="accumulated" select="0"/>
<xsl:choose>
<xsl:when test="$number">
<xsl:variable name="n" select="$number mod 10" />
<xsl:variable name="m" select="substring($factors, 1, 1)" />
<xsl:call-template name="process">
<xsl:with-param name="number" select="floor($number div 10)"/>
<xsl:with-param name="factors" select="substring($factors, 2)"/>
<xsl:with-param name="accumulated" select="$accumulated + $n * $m" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$accumulated"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Example of call:
<xsl:call-template name="process">
<xsl:with-param name="number" select="28200703"/>
</xsl:call-template>
Demo: http://xsltransform.net/3MvmrAF
This is all about reverse a string. It works properly for the given Value 'ABCDEF'. The output is also correct 'FEDCBA'. But I want to know how this is printing letters 'A' and 'D' in this string. Could anyone help me to understand this? please.
Elaborate this working method.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output method="html"/>
<xsl:template name="reverse">
<xsl:param name="input" select="'ABCDEF'"/>
<xsl:variable name="len" select="string-length($input)"/>
<xsl:choose>
<xsl:when test="$len < 2">
<xsl:value-of select="$input"/>
</xsl:when>
<xsl:when test="$len = 2">
<xsl:value-of select="substring($input,2,1)"/>
<xsl:value-of select="substring($input,1,1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="mid" select="floor($len div 2)"/>
<xsl:call-template name="reverse">
<xsl:with-param name="input" select="substring($input,$mid+1,$mid+1)"/>
</xsl:call-template>
<xsl:call-template name="reverse">
<xsl:with-param name="input" select="substring($input,1,$mid)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="reverse">
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Here's what happens:
Input ABCDEF
len = 6
len is > 2 and not = 2
mid = 3
Call again with input DEF
len = 3
len is > 2 and not = 2
mid = 1
Call again with input EF
len = 2
len is not > 2 but = 2
Put out the second letter F
Put out the first letter E
Return to outer call
Call with input = D
len is less than 2
Output D
Return to outer call
Call again with input = ABC
len = 3
len is > 2 and not 2
mid = 1
Call again with input BC
len = 2
len is not > 2 but = 2
Put out the second letter C
Put out the first letter B
Return to outer call
Call with input A
len is less than 2
Output A
I am trying to increment a counter when a condition is met, for example if a final_score is over 30, count = 1. so if 3 scores are over 30 the final_score should be 3. To get the scores i need to add previous scores so the final score is the calculation of all the other scores
for example
Example XML
<scores>
<score result="20" />
<score result="10" />
<score redult="5" />
</score>
Main XSL
<xsl:call-template name="scores">
<xsl:with-param name="prev_count" select="0"/>
</xsl:call-template>
Template for scores
<template name="scores">
<param name="counter" select="0">
<xsl:variable name="prev_counter" select="$counter+1" />
<xsl:for-each select="persons/person[id=#id]">//get all the scores
<xsl:varable name="total_score" select="sum(scores/score)" />//sum them all up
<xsl:if test="total_score > 40" > //check the result
<xsl:value-of select="$counter">
<xsl:call-template name="scores">
<xsl:with-param name="counter" select="$counter+1"/>
</xsl:call-template>
</xsl:if>
<xsl:for-each>
</xsl:template>
</xsl:template>
I just cannot work it out, or is my approach way off?
If I have understood correctly, to count the number of person elements, where the total of the score elements is greater than 30, just use this expression (assuming you are currently positioned on the parent persons element)
<xsl:value-of select="count(person[sum(scores/score/#result) > 30])" />