Re: [xsl] Find the number of elements that are prior to the series of elements that match a string?

Subject: Re: [xsl] Find the number of elements that are prior to the series of elements that match a string?
From: "Dimitre Novatchev dnovatchev@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Tue, 12 Mar 2019 20:20:39 -0000
Here is an XSLT 2.0 solution using keys. In its current form it
returns the wanted offset (length) as a hexadecimal number. To convert
this to a decimal number, have a look at the archives where AFAIR
there is such code -- could be even posted by me ... This conversion
to decimal can certainly be written elegantly as fold-left() in
XSLT/XPath 3

Also, the key is not generated based on the length of the searched
string (as in Elliot's solution), because I believe the transformation
will be more useful if it can process multiple searches with varying
lengths during the life of the transformation.

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kByteByVal" match="Byte" use="."/>

 <xsl:param name="pTarget" select="'04000000FF'"/>

  <xsl:template match="/">
    <xsl:variable name="vMatchingHeads" select="key('kByteByVal',
substring($pTarget, 1,2))" as="node()*"/>
    <xsl:variable name="vSolution" select=
    "$vMatchingHeads[string-join(. |
following-sibling::*[not(position() ge string-length($pTarget) idiv
2)], '') eq $pTarget][1]"/>

    <xsl:value-of select="$vSolution/@hexAddress"/>
  </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document
(similar to the originally provided one, but added another 04 to make
the case more realistic:

<Bytes>
    <Byte hexAddress="0">4D</Byte>
    <Byte hexAddress="1">5A</Byte>
    <Byte hexAddress="2">90</Byte>
    <Byte hexAddress="3">00</Byte>
    <Byte hexAddress="4">03</Byte>
    <Byte hexAddress="5">04</Byte>
    <Byte hexAddress="6">00</Byte>
    <Byte hexAddress="7">00</Byte>
    <Byte hexAddress="8">00</Byte>
    <Byte hexAddress="9">04</Byte>
    <Byte hexAddress="A">00</Byte>
    <Byte hexAddress="B">00</Byte>
    <Byte hexAddress="C">00</Byte>
    <Byte hexAddress="D">FF</Byte>
    <Byte hexAddress="E">FF</Byte>
</Bytes>

the wanted result is produced:

9


Cheers,
Dimitre

On Tue, Mar 12, 2019 at 11:01 AM Michael Kay mike@xxxxxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
>
>
> My instinct is to use index-of() to get the positions of the elements that
match the first required byte, and then for each of these positions, form the
string of N bytes starting at this position and do a string comparison to test
whether there is a match at this position.
>
> let $candidates := index-of(/Bytes/Byte, substring($req, 1, 2))
> return $candidates[string-join(subsequence(/Bytes/Byte, ., $len), '') eq
$req][1] - 1
>
> Michael Kay
> Saxoniica
>
>
>
> > On 12 Mar 2019, at 17:35, Costello, Roger L. costello@xxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
> >
> > Hi Folks,
> >
> > I have an XML document containing a long list of Byte elements, e.g.,
> >
> > <Bytes>
> >    <Byte hexAddress="0">4D</Byte>
> >    <Byte hexAddress="1">5A</Byte>
> >    <Byte hexAddress="2">90</Byte>
> >    <Byte hexAddress="3">00</Byte>
> >    <Byte hexAddress="4">03</Byte>
> >    <Byte hexAddress="5">00</Byte>
> >    <Byte hexAddress="6">00</Byte>
> >    <Byte hexAddress="7">00</Byte>
> >    <Byte hexAddress="8">04</Byte>
> >    <Byte hexAddress="9">00</Byte>
> >    <Byte hexAddress="A">00</Byte>
> >    <Byte hexAddress="B">00</Byte>
> >    <Byte hexAddress="C">FF</Byte>
> >    <Byte hexAddress="D">FF</Byte>
> >    ...
> > </Bytes>
> >
> > I have a string of hex values, e.g.,
> >
> > 04000000FF
> >
> > You can see that the string is contained within this series of Byte
elements:
> >
> >    <Byte hexAddress="8">04</Byte>
> >    <Byte hexAddress="9">00</Byte>
> >    <Byte hexAddress="A">00</Byte>
> >    <Byte hexAddress="B">00</Byte>
> >    <Byte hexAddress="C">FF</Byte>
> >
> > I want to find the number of <Byte> elements that occur prior to the
series of Byte elements.
> >
> > I figured one approach is to convert all the Byte text node values into a
string and then perform string manipulations:
> >
> > string-length(substring-before(string-join(/Bytes/Byte/text(), ''),
'04000000FF')) idiv 2
> >
> > Note: I divided by two because each Byte has 2 digits.
> >
> > That seems to work, but I suspect it is horribly inefficient because it
creates a string containing all the Byte text node values and there may be
tens or hundreds of thousands of Byte elements.
> >
> > What is an efficient way to solve this problem?
> >
> > /Roger
> >
> >
> >
> >
>



--
Cheers,
Dimitre Novatchev
---------------------------------------
Truly great madness cannot be achieved without significant intelligence.
---------------------------------------
To invent, you need a good imagination and a pile of junk
-------------------------------------
Never fight an inanimate object
-------------------------------------
To avoid situations in which you might make mistakes may be the
biggest mistake of all
------------------------------------
Quality means doing it right when no one is looking.
-------------------------------------
You've achieved success in your field when you don't know whether what
you're doing is work or play
-------------------------------------
To achieve the impossible dream, try going to sleep.
-------------------------------------
Facts do not cease to exist because they are ignored.
-------------------------------------
Typing monkeys will write all Shakespeare's works in 200yrs.Will they
write all patents, too? :)
-------------------------------------
Sanity is madness put to good use.
-------------------------------------
I finally figured out the only reason to be alive is to enjoy it.

Current Thread