|
Subject: Re: [xsl] Re: Creating sequence/range text from position From: "Dimitre Novatchev dnovatchev@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> Date: Sun, 29 Jan 2023 02:47:35 -0000 |
Here is a pure XPath 3.1 (that means also XQuery) solution.
The bulk of the logic is in the function $compactInner, which is just 24
lines.
let $doc :=
<article>
<p>Case 1 is <xref ref-type="bibr" rid="r2"/></p>
<p>Case 2 is <xref ref-type="bibr" rid="r1 r2 r3 r5 r6 r8 r9 r10"/></p>
<p>Case 3 is <xref ref-type="bibr" rid="r1 r2 r3 r4"/></p>
<p>Case 4 is <xref ref-type="bibr" rid="r1 r3 r5 r7"/></p>
<back>
<ref-list>
<ref id="r1">...</ref>
<ref id="r2">...</ref>
<ref id="r3">...</ref>
<ref id="r4">...</ref>
<ref id="r5">...</ref>
<ref id="r6">...</ref>
<ref id="r7">...</ref>
<ref id="r8">...</ref>
<ref id="r9">...</ref>
<ref id="r10">...</ref>
</ref-list>
</back>
</article>,
$compactInner := function($ar as array(xs:integer), $self as function(*))
{
let $arSize := array:size($ar),
$arExists := exists($ar) and $arSize gt 0
return
if(not($arExists)) then ()
else
let $start := $ar(1),
$next := (for $i in 2 to $arSize
return
if($ar($i) ge $start + $i)
then $i
else ()
)[1],
$nextEnd := if(exists($next)) then $next -1 else $arSize,
$upper := $ar($nextEnd)
return ($start || (if($arSize gt 1 and $upper ne $start)
then'-' || $upper else ()),
if(exists($next))
then $self(array:subarray($ar, $next), $self)
else if($arSize gt $nextEnd)
then $self(array:subarray($ar, $nextEnd +1),
$self)
else ()
)
},
$compact := function($ar as array(xs:integer))
{
$compactInner($ar, $compactInner)
},
$summary := ->($pIn as xs:string)
{
let $arr := array{ tokenize(replace($pIn, 'r', ''), ' ') !
xs:integer(.) }
return $compact($arr)
}
return
($summary($doc/p[1]/xref[1]/@rid),
"------------",
$summary($doc/p[2]/xref[1]/@rid),
"------------",
$summary($doc/p[3]/xref[1]/@rid),
"------------",
$summary($doc/p[4]/xref[1]/@rid)
)
When the above XPath expression is evaluated, the expected, correct results
are produced:
2
------------
1-3
5-6
8-10
------------
1-4
------------
1
3
5
7
Note: This compactor expresses any adjacent and increasing subsequence with
the "-" notation. If you need to use the "-" notation only for subsequences
with length at least 3, then this is left as an exercise for the reader :)
The same for data where the ref. numbers are not in increasing order (hint:
use the standard XPath 3.1 sort() function as a start)
Thanks,
Dimitre
On Sat, Jan 28, 2023 at 8:50 AM Charles O'Connor coconnor@xxxxxxxxxxxx <
xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
> Thanks Vincent, and great to see you at NISO meetings!
>
>
>
> This works great! I will now spend a couple hours figuring out how you did
> it (or trying to), because thatbs how Ibve learned the little XSLT that
I
> know.
>
>
>
> Best,
>
> Charles
>
>
>
> *From:* Lizzi, Vincent vincent.lizzi@xxxxxxxxxxxxxxxxxxxx <
> xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
> *Sent:* Saturday, January 28, 2023 12:50 AM
> *To:* xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> *Subject:* [xsl] Re: Creating sequence/range text from position
>
>
>
> *** External email: use caution ***
>
>
>
> Hi Charles,
>
>
>
> Here is a slightly improved version that produces the expected output from
> your sample input and also copes with a case of the idbs listed in @rid
not
> being in sequential order.
>
>
>
> <?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:mode on-no-match="shallow-copy"/>
>
>
>
> <xsl:key name="element-by-id" match="*[@id]" use="@id"/>
>
>
>
> <xsl:template name="label" as="element(label)">
>
> <xsl:param name="id" as="xs:string"/>
>
> <xsl:param name="context" as="node()" select="root(.)"/>
>
> <label><xsl:value-of select="1 + count(key('element-by-id', $id,
> $context)/preceding::ref)"></xsl:value-of></label>
>
> </xsl:template>
>
>
>
> <xsl:template match="ref">
>
> <xsl:copy>
>
> <xsl:apply-templates select="@*"/>
>
> <xsl:call-template name="label">
>
> <xsl:with-param name="id" select="@id"/>
>
> </xsl:call-template>
>
> <xsl:apply-templates select="node()"/>
>
> </xsl:copy>
>
> </xsl:template>
>
>
>
> <xsl:template match="xref[not(node())]">
>
> <xsl:copy>
>
> <xsl:apply-templates select="@*"/>
>
> <xsl:variable name="context" select="root(.)"/>
>
> <xsl:variable name="labels" as="element(label)*">
>
> <xsl:for-each select="tokenize(@rid)">
>
> <xsl:call-template name="label">
>
> <xsl:with-param name="id" select="."/>
>
> <xsl:with-param name="context" select="$context"/>
>
> </xsl:call-template>
>
> </xsl:for-each>
>
> </xsl:variable>
>
> <xsl:variable name="sorted" as="element(labels)">
>
> <labels>
>
> <xsl:for-each select="$labels">
>
> <xsl:sort select="number()"/>
>
> <xsl:sequence select="."/>
>
> </xsl:for-each>
>
> </labels>
>
> </xsl:variable>
>
> <xsl:variable name="formatted" as="xs:string*">
>
> <xsl:for-each select="$sorted/label">
>
> <xsl:choose>
>
> <xsl:when test="not(preceding::label)">
>
> <xsl:sequence select="string()"/>
>
> </xsl:when>
>
> <xsl:when test="number() - 1 eq
> number(preceding::label[1])
>
> and number() + 1 eq
> number(following::label[1])"/>
>
> <xsl:when test="number() - 1 eq
> number(preceding::label[1])
>
> and number() - 2 eq
> number(preceding::label[2])">
>
> <xsl:sequence select="'-' || string()"/>
>
> </xsl:when>
>
> <xsl:when test="preceding::label">
>
> <xsl:sequence select="',' || string()"/>
>
> </xsl:when>
>
> </xsl:choose>
>
> </xsl:for-each>
>
> </xsl:variable>
>
> <xsl:value-of select="string-join($formatted, '')"/>
>
> </xsl:copy>
>
> </xsl:template>
>
> </xsl:stylesheet>
>
>
>
> Best wishes,
>
> Vincent
>
>
>
> _____________________________________________
>
> *Vincent M. Lizzi*
>
> Head of Information Standards | Taylor & Francis Group
>
> vincent.lizzi@xxxxxxxxxxxxxxxxxxxx
>
>
>
>
>
>
>
> Information Classification: General
>
> *From:* Charles O'Connor coconnor@xxxxxxxxxxxx <
> xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
> *Sent:* Friday, January 27, 2023 7:05 PM
> *To:* xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> *Subject:* [xsl] Creating sequence/range text from position
>
>
>
> Hi all,
>
> We use a word processor-like XML editor that presents users with generated
> text for numbered bibliographic references and their in-text citations.
> However, downstream systems require actual text to be placed. Question is,
> how do we get it?
>
> (Using SaxonEE 10.x)
>
> Given
>
> <article>
> <p>Case 1 is <xref ref-type="bibr" rid="r2"/></p>
> <p>Case 2 is <xref ref-type="bibr" rid="r1 r2 r3 r5 r6 r8 r9 r10"/></p>
> <back>
> <ref-list>
> <ref id="r1">...</ref>
> <ref id="r2">...</ref>
> <ref id="r3">...</ref>
> <ref id="r4">...</ref>
> <ref id="r5">...</ref>
> <ref id="r6">...</ref>
> <ref id="r7">...</ref>
> <ref id="r8">...</ref>
> <ref id="r9">...</ref>
> <ref id="r10">...</ref>
> </ref-list>
> </back>
> </article>
>
> We want
>
> <article>
> <p>Case 1 is <xref ref-type="bibr" rid="r2">2</xref></p>
> <p>Case 2 is <xref ref-type="bibr" rid="r1 r2 r3 r5 r6 r8 r9
> r10">1-3,5,6,8-10</xref></p>
> <back>
> <ref-list>
> <ref id="r1">...</ref>
> <ref id="r2">...</ref>
> <ref id="r3">...</ref>
> <ref id="r4">...</ref>
> <ref id="r5">...</ref>
> <ref id="r6">...</ref>
> <ref id="r7">...</ref>
> <ref id="r8">...</ref>
> <ref id="r9">...</ref>
> <ref id="r10">...</ref>
> </ref-list>
> </back>
> </article>
>
> (only the xrefs have changed)
>
> Case 1 is pretty easily handled with:
>
> <xsl:template match="xref[@ref-type='bibr']">
> <xsl:variable name="rid" select="@rid" />
> <xsl:copy>
> <xsl:copy-of select="@*" />
> <xsl:value-of
> select="count(/article/back/ref-list/ref[@id=$rid]/preceding-sibling::ref)
> + 1" />
> </xsl:copy>
> </xsl:template>
>
> But Case 2 is not even close. I tried adapting the solution here, to no
> avail:
>
https://stackoverflow.com/questions/47559712/xslt-sequence-of-numbers-to-rang
e
>
> Whatever I did, I just got the first number and none of the rest. The
> extra twist here is that numbers should only be expressed as a range when 3
> or more occur sequentially.
>
> Thanks,
> Charles
>
> Charles O'Connor l Lead Product Manager
> Pronouns: He/Him
> Aries Systems Corporation l www.ariessys.com
> 50 High Street, Suite 21 l North Andover, MA l 01845 l USA
>
>
> Main: +1 (978) 975-7570
> Cell: +1 (802) 585-5655
>
>
>
> XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list>
>
> EasyUnsubscribe <http://lists.mulberrytech.com/unsub/xsl-list/2963104> (by
> email)
> XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list>
> EasyUnsubscribe <http://lists.mulberrytech.com/unsub/xsl-list/782854> (by
> email <>)
| Current Thread |
|---|
|
| <- Previous | Index | Next -> |
|---|---|---|
| [xsl] Re: Creating sequence/range t, Charles O'Connor coc | Thread | [xsl] cursed RTF outputs, Jean-Paul Rehr rehrj |
| Re: [xsl] cursed RTF outputs, Jean-Paul Rehr rehrj | Date | [xsl] matches() method in XPath 1.0, Manuel Souto Pico te |
| Month |