Counting

Friday, 27 January 2017 · 11 messages · last reply 29 Jan 2017
M
meumapple27 Jan 2017 09:08 #
Hi, I have a structure like this:
<c>
<a>red</a>
<a>red</a>
<a>blue</a>
<a>green</a>
<a>green</a>
</c>
And I would like to get:
<c>
<a n="1">red</a>
<a n="2">red</a>
<a n="1">blue</a>
<a n="1">green</a>
<a n="2">green</a>
</c>
I have tried with two for loops after having identified the distinct-values(). This is in principle correct but it takes too long (I have many distinct values and a elements), because I cannot exit the for loop as soon as I get a match. I have been able to get the result with the window clause and then counting the elements of the window with a nested for-loop: is there an alternative solution? Thanks. Joseph
WS
Wiemer, Sebastian27 Jan 2017 12:21 #
Hi Joseph, I hope this helps:
let $c := <c><a>red</a><a>red</a><a>blue</a><a>green</a><a>green</a></c>
return
  <c>

    {

       for $color-group in $c/a

       let $color := $color-group/text()
group by $color return
         for $color at $color-count in $color-group
return <a n="{$color-count}">{$color/text()}</a> }
</c>
best regards, Sebastian
Quoted text · meumapple · 52 lines
> Hi, > > > > I have a structure like this: > > > > <c> > > <a>red</a> > > <a>red</a> > > <a>blue</a> > > <a>green</a> > > <a>green</a> > > </c> > > > > And I would like to get: > > > > <c> > > <a n="1">red</a> > > <a n="2">red</a> > > <a n="1">blue</a> > > <a n="1">green</a> > > <a n="2">green</a> > > </c> > > > > I have tried with two for loops after having identified the distinct-values(). This is in principle correct but it takes too long (I have many distinct values and a elements), because I cannot exit the for loop as soon as I get a match. I have been able to > get the result with the window clause and then counting the elements of the window with a nested for-loop: is there an alternative solution? Thanks. > > > > Joseph ------------------------------------------------------- >>> business. people. technology. <<<
------------------------------------------------------- adesso AG mit Sitz in Dortmund Vorstand: Michael Kenfenheuer (Vors.), Christoph Junge, Andreas Prenneis Vorsitzender des Aufsichtsrates: Prof. Dr. Volker Gruhn Amtsgericht Dortmund HRB 20663
ML
Marco Letterein reply to meumapple, 27 Jan 2017, 09:0827 Jan 2017 12:23 #
Hi Joseph, what about this?
let $input :=
<c>
<a>red</a>
<a>red</a>
<a>blue</a>
<a>green</a>
<a>green</a>
</c>

return
   <c>{
     for $a in $input/*
     let $t := $a/text()
     group by $t
     return
       for $a2 at $pos in $a
       return <a n="{$pos}">{$a2/text()}</a>
   }</c>
Ciao, M.
Quoted text · meumapple · 26 lines
On 27/01/2017 09:08, meumapple wrote: > Hi, > > I have a structure like this: > > <c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > And I would like to get: > > <c> > <a n="1">red</a> > <a n="2">red</a> > <a n="1">blue</a> > <a n="1">green</a> > <a n="2">green</a> > </c> > > I have tried with two for loops after having identified the distinct-values(). This is in principle correct but it takes too long (I have many distinct values and a elements), because I cannot exit the for loop as soon as I get a match. I have been able to get the result with the window clause and then counting the elements of the window with a nested for-loop: is there an alternative solution? Thanks. > > Joseph
AB
Andy Bunce27 Jan 2017 12:33 #
Hi Joseph, You could see how XQuery update (http://docs.basex.org/wiki/Update) performs...
let $src:=<c>
<a>red</a>
<a>red</a>
<a>blue</a>
<a>green</a>
<a>green</a>
</c>

return copy $result:=$src
          modify ( for $a in $result/a
                      return insert node attribute { 'n' } {
1+count($a/preceding-sibling::a[.=$a])} into $a)
         return $result
/Andy
Quoted text · Marco Lettere · 63 lines
On 27 January 2017 at 11:23, Marco Lettere <m.lettere at gmail.com> wrote: > Hi Joseph, > what about this? > > let $input := > <c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > return > <c>{ > for $a in $input/* > let $t := $a/text() > group by $t > return > for $a2 at $pos in $a > return <a n="{$pos}">{$a2/text()}</a> > }</c> > > Ciao, > M. > > > On 27/01/2017 09:08, meumapple wrote: > >> Hi, >> >> I have a structure like this: >> >> <c> >> <a>red</a> >> <a>red</a> >> <a>blue</a> >> <a>green</a> >> <a>green</a> >> </c> >> >> And I would like to get: >> >> <c> >> <a n="1">red</a> >> <a n="2">red</a> >> <a n="1">blue</a> >> <a n="1">green</a> >> <a n="2">green</a> >> </c> >> >> I have tried with two for loops after having identified the >> distinct-values(). This is in principle correct but it takes too long (I >> have many distinct values and a elements), because I cannot exit the for >> loop as soon as I get a match. I have been able to get the result with the >> window clause and then counting the elements of the window with a nested >> for-loop: is there an alternative solution? Thanks. >> >> Joseph >> > > >
M
meumapple29 Jan 2017 09:50 #
Thanks to all of you. The group by clause works, while Andy's solution does not seem to produce the right result on my machine. The group by clause, however, moves the elements for the grouping. I was wondering whether it would be possible to count them without moving, and more in general if there is a way to get more control over 2 for-loops combined, so that once a condition is satisfied it is possible to stop a certain repetition. Thanks! Joseph
Quoted text · Andy Bunce · 1 line
Il giorno 27 gen 2017, alle ore 12:33, Andy Bunce <bunce.andy at gmail.com> ha scritto:
Hi Joseph, You could see how XQuery update (http://docs.basex.org/wiki/Update) performs...
let $src:=<c>
<a>red</a>
<a>red</a>
<a>blue</a>
<a>green</a>
<a>green</a>
</c>

return copy $result:=$src
          modify ( for $a in $result/a
                      return insert node attribute { 'n' } { 1+count($a/preceding-sibling::a[.=$a])} into $a)
         return $result
/Andy
Quoted text · Marco Lettere · 53 lines
> On 27 January 2017 at 11:23, Marco Lettere <m.lettere at gmail.com> wrote: > Hi Joseph, > what about this? > > let $input := > <c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > return > <c>{ > for $a in $input/* > let $t := $a/text() > group by $t > return > for $a2 at $pos in $a > return <a n="{$pos}">{$a2/text()}</a> > }</c> > > Ciao, > M. > > >> On 27/01/2017 09:08, meumapple wrote: >> Hi, >> >> I have a structure like this: >> >> <c> >> <a>red</a> >> <a>red</a> >> <a>blue</a> >> <a>green</a> >> <a>green</a> >> </c> >> >> And I would like to get: >> >> <c> >> <a n="1">red</a> >> <a n="2">red</a> >> <a n="1">blue</a> >> <a n="1">green</a> >> <a n="2">green</a> >> </c> >> >> I have tried with two for loops after having identified the distinct-values(). This is in principle correct but it takes too long (I have many distinct values and a elements), because I cannot exit the for loop as soon as I get a match. I have been able to get the result with the window clause and then counting the elements of the window with a nested for-loop: is there an alternative solution? Thanks. >> >> Joseph
AB
Andy Bunce29 Jan 2017 12:58 #
Hi Joseph,
Quoted text · meumapple · 1 line
>while Andy's solution does not seem to produce the right result on my
machine. Do you mean you get a different result with the example data or it is not working with your real data? Regards /Andy I am using BaseX 8.6 ----------------------------------- let $src:=<c><a>red</a><a>red</a><a>blue</a><a>green</a><a>green</a></c>
return copy $result:=$src
          modify ( for $a in $result/a
                      return insert node attribute { 'n' } {
1+count($a/preceding-sibling::a[.=$a])} into $a)
         return $result
----------------------------------
<c>
  <a n="1">red</a>
  <a n="2">red</a>
  <a n="1">blue</a>
  <a n="1">green</a>
  <a n="2">green</a>
</c>
Quoted text · meumapple · 100 lines
On 29 January 2017 at 08:50, meumapple <meumapple at gmail.com> wrote: > Thanks to all of you. The group by clause works, while Andy's solution > does not seem to produce the right result on my machine. The group by > clause, however, moves the elements for the grouping. I was wondering > whether it would be possible to count them without moving, and more in > general if there is a way to get more control over 2 for-loops combined, so > that once a condition is satisfied it is possible to stop a certain > repetition. > > Thanks! > Joseph > > Il giorno 27 gen 2017, alle ore 12:33, Andy Bunce <bunce.andy at gmail.com> > ha scritto: > > Hi Joseph, > You could see how XQuery update (http://docs.basex.org/wiki/Update) > performs... > > let $src:=<c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > return copy $result:=$src > modify ( for $a in $result/a > return insert node attribute { 'n' } { > 1+count($a/preceding-sibling::a[.=$a])} into $a) > return $result > > /Andy > > On 27 January 2017 at 11:23, Marco Lettere <m.lettere at gmail.com> wrote: > >> Hi Joseph, >> what about this? >> >> let $input := >> <c> >> <a>red</a> >> <a>red</a> >> <a>blue</a> >> <a>green</a> >> <a>green</a> >> </c> >> >> return >> <c>{ >> for $a in $input/* >> let $t := $a/text() >> group by $t >> return >> for $a2 at $pos in $a >> return <a n="{$pos}">{$a2/text()}</a> >> }</c> >> >> Ciao, >> M. >> >> >> On 27/01/2017 09:08, meumapple wrote: >> >>> Hi, >>> >>> I have a structure like this: >>> >>> <c> >>> <a>red</a> >>> <a>red</a> >>> <a>blue</a> >>> <a>green</a> >>> <a>green</a> >>> </c> >>> >>> And I would like to get: >>> >>> <c> >>> <a n="1">red</a> >>> <a n="2">red</a> >>> <a n="1">blue</a> >>> <a n="1">green</a> >>> <a n="2">green</a> >>> </c> >>> >>> I have tried with two for loops after having identified the >>> distinct-values(). This is in principle correct but it takes too long (I >>> have many distinct values and a elements), because I cannot exit the for >>> loop as soon as I get a match. I have been able to get the result with the >>> window clause and then counting the elements of the window with a nested >>> for-loop: is there an alternative solution? Thanks. >>> >>> Joseph >>> >> >> >> >
LW
Leonard Wörtelerin reply to meumapple, 29 Jan 2017, 09:5029 Jan 2017 13:03 #
Hi Joseph, if the equal elements are always grouped together, you can use the window clause to gather them into a window and then iterate over that to get the numbers:
     let $src:=
       <c>
         <a>red</a>
         <a>red</a>
         <a>blue</a>
         <a>green</a>
         <a>green</a>
       </c>
     for tumbling window $window in $src/*
         start $s when true()
         end next $n when $s/text() ne $n/text()
     for $elem at $pos in $window
     return <a n="{$pos}">{$elem/node()}</a>
For full control over the state you want to maintain during the iteration, you can use fn:fold-left and pass around a map in which you count how often you've encountered each distinct value:
     let $src:=
       <c>
         <a>red</a>
         <a>red</a>
         <a>blue</a>
         <a>green</a>
         <a>green</a>
       </c>
     return tail(
       fold-left(
         $src/*,
         map{},
         function($acc, $a) {
           let $key := $a/string(),
               $nums := $acc[1],
               $n := ($nums($key), 0)[1] + 1
           return (
             map:merge(($nums, map{ $key: $n })),
             tail($acc),
             <a n="{$n}">{$key}</a>
           )
         }
       )
     )
One element element at a time is added to the accumulator, which consists of the map of counts followed by all elements to be output. Hope that helps, Leo
Quoted text · meumapple · 101 lines
On 29.01.2017 at 09:50, meumapple wrote (with possible deletions): > Thanks to all of you. The group by clause works, while Andy's solution > does not seem to produce the right result on my machine. The group by > clause, however, moves the elements for the grouping. I was wondering > whether it would be possible to count them without moving, and more in > general if there is a way to get more control over 2 for-loops combined, > so that once a condition is satisfied it is possible to stop a certain > repetition. > > Thanks! > Joseph > > Il giorno 27 gen 2017, alle ore 12:33, Andy Bunce <bunce.andy at gmail.com > <mailto:bunce.andy at gmail.com>> ha scritto: > > Hi Joseph, > You could see how XQuery update (http://docs.basex.org/wiki/Update) > performs... > > let $src:=<c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > return copy $result:=$src > modify ( for $a in $result/a > return insert node attribute { 'n' } { > 1+count($a/preceding-sibling::a[.=$a])} into $a) > return $result > > /Andy > > On 27 January 2017 at 11:23, Marco Lettere <m.lettere at gmail.com > <mailto:m.lettere at gmail.com>> wrote: > > Hi Joseph, > what about this? > > let $input := > <c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > return > <c>{ > for $a in $input/* > let $t := $a/text() > group by $t > return > for $a2 at $pos in $a > return <a n="{$pos}">{$a2/text()}</a> > }</c> > > Ciao, > M. > > > On 27/01/2017 09:08, meumapple wrote: > > Hi, > > I have a structure like this: > > <c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > And I would like to get: > > <c> > <a n="1">red</a> > <a n="2">red</a> > <a n="1">blue</a> > <a n="1">green</a> > <a n="2">green</a> > </c> > > I have tried with two for loops after having identified the > distinct-values(). This is in principle correct but it takes too > long (I have many distinct values and a elements), because I > cannot exit the for loop as soon as I get a match. I have been > able to get the result with the window clause and then counting > the elements of the window with a nested for-loop: is there an > alternative solution? Thanks. > > Joseph > > > >
M
meumapplein reply to Andy Bunce, 29 Jan 2017, 12:5829 Jan 2017 14:37 #
The result is not the one I look for
Quoted text · Andy Bunce · 1 line
Il giorno 29 gen 2017, alle ore 12:58, Andy Bunce <bunce.andy at gmail.com> ha scritto:
Hi Joseph,
Quoted text · Andy Bunce · 1 line
>while Andy's solution does not seem to produce the right result on my machine.
Do you mean you get a different result with the example data or it is not working with your real data? Regards /Andy I am using BaseX 8.6 ----------------------------------- let $src:=<c><a>red</a><a>red</a><a>blue</a><a>green</a><a>green</a></c>
return copy $result:=$src
          modify ( for $a in $result/a
                      return insert node attribute { 'n' } { 1+count($a/preceding-sibling::a[.=$a])} into $a)
         return $result
----------------------------------
<c>
  <a n="1">red</a>
  <a n="2">red</a>
  <a n="1">blue</a>
  <a n="1">green</a>
  <a n="2">green</a>
</c>
Quoted text · meumapple · 79 lines
> On 29 January 2017 at 08:50, meumapple <meumapple at gmail.com> wrote: > Thanks to all of you. The group by clause works, while Andy's solution does not seem to produce the right result on my machine. The group by clause, however, moves the elements for the grouping. I was wondering whether it would be possible to count them without moving, and more in general if there is a way to get more control over 2 for-loops combined, so that once a condition is satisfied it is possible to stop a certain repetition. > > Thanks! > Joseph > > Il giorno 27 gen 2017, alle ore 12:33, Andy Bunce <bunce.andy at gmail.com> ha scritto: > > Hi Joseph, > You could see how XQuery update (http://docs.basex.org/wiki/Update) performs... > > let $src:=<c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > return copy $result:=$src > modify ( for $a in $result/a > return insert node attribute { 'n' } { 1+count($a/preceding-sibling::a[.=$a])} into $a) > return $result > > /Andy > >> On 27 January 2017 at 11:23, Marco Lettere <m.lettere at gmail.com> wrote: >> Hi Joseph, >> what about this? >> >> let $input := >> <c> >> <a>red</a> >> <a>red</a> >> <a>blue</a> >> <a>green</a> >> <a>green</a> >> </c> >> >> return >> <c>{ >> for $a in $input/* >> let $t := $a/text() >> group by $t >> return >> for $a2 at $pos in $a >> return <a n="{$pos}">{$a2/text()}</a> >> }</c> >> >> Ciao, >> M. >> >> >>> On 27/01/2017 09:08, meumapple wrote: >>> Hi, >>> >>> I have a structure like this: >>> >>> <c> >>> <a>red</a> >>> <a>red</a> >>> <a>blue</a> >>> <a>green</a> >>> <a>green</a> >>> </c> >>> >>> And I would like to get: >>> >>> <c> >>> <a n="1">red</a> >>> <a n="2">red</a> >>> <a n="1">blue</a> >>> <a n="1">green</a> >>> <a n="2">green</a> >>> </c> >>> >>> I have tried with two for loops after having identified the distinct-values(). This is in principle correct but it takes too long (I have many distinct values and a elements), because I cannot exit the for loop as soon as I get a match. I have been able to get the result with the window clause and then counting the elements of the window with a nested for-loop: is there an alternative solution? Thanks. >>> >>> Joseph
AB
Andy Bunce29 Jan 2017 14:56 #
Quoted text · meumapple · 1 line
>The result is not the one I look for
Ok. If you only want to count adjacent equals then the code below should do that. Although preceding-sibling has some performance questions (for example https://mailman.uni-konstanz.de/pipermail/basex-talk/2011-October/002068.html ) and I am sure Leonard's approach is the most functional and flexible. /Andy
let $src:=<c>
  <a>red</a>
  <a>red</a>
  <a>blue</a>
  <a>green</a>
  <a>green</a>
  <a>red</a>
  <a>red</a>
</c>
return copy $result:=$src
          modify ( for $a in $result/a
                   let
$diff:=($a/preceding-sibling::a[not(.=$a)],$result)[1] (:closest not equal
or root:)
                   return insert node attribute { 'n' } {
1+count($a/preceding-sibling::a[. >> $diff and . =$a ])} into $a)
         return $result
Quoted text · meumapple · 136 lines
On 29 January 2017 at 13:37, meumapple <meumapple at gmail.com> wrote: > The result is not the one I look for > > Il giorno 29 gen 2017, alle ore 12:58, Andy Bunce <bunce.andy at gmail.com> > ha scritto: > > Hi Joseph, > > >while Andy's solution does not seem to produce the right result on my > machine. > Do you mean you get a different result with the example data or it is not > working with your real data? > > Regards > /Andy > > I am using BaseX 8.6 > ----------------------------------- > let $src:=<c><a>red</a><a>red</a><a>blue</a><a>green</a><a>green</a></c> > > return copy $result:=$src > modify ( for $a in $result/a > return insert node attribute { 'n' } { > 1+count($a/preceding-sibling::a[.=$a])} into $a) > return $result > ---------------------------------- > <c> > <a n="1">red</a> > <a n="2">red</a> > <a n="1">blue</a> > <a n="1">green</a> > <a n="2">green</a> > </c> > > On 29 January 2017 at 08:50, meumapple <meumapple at gmail.com> wrote: > >> Thanks to all of you. The group by clause works, while Andy's solution >> does not seem to produce the right result on my machine. The group by >> clause, however, moves the elements for the grouping. I was wondering >> whether it would be possible to count them without moving, and more in >> general if there is a way to get more control over 2 for-loops combined, so >> that once a condition is satisfied it is possible to stop a certain >> repetition. >> >> Thanks! >> Joseph >> >> Il giorno 27 gen 2017, alle ore 12:33, Andy Bunce <bunce.andy at gmail.com> >> ha scritto: >> >> Hi Joseph, >> You could see how XQuery update (http://docs.basex.org/wiki/Update) >> performs... >> >> let $src:=<c> >> <a>red</a> >> <a>red</a> >> <a>blue</a> >> <a>green</a> >> <a>green</a> >> </c> >> >> return copy $result:=$src >> modify ( for $a in $result/a >> return insert node attribute { 'n' } { >> 1+count($a/preceding-sibling::a[.=$a])} into $a) >> return $result >> >> /Andy >> >> On 27 January 2017 at 11:23, Marco Lettere <m.lettere at gmail.com> wrote: >> >>> Hi Joseph, >>> what about this? >>> >>> let $input := >>> <c> >>> <a>red</a> >>> <a>red</a> >>> <a>blue</a> >>> <a>green</a> >>> <a>green</a> >>> </c> >>> >>> return >>> <c>{ >>> for $a in $input/* >>> let $t := $a/text() >>> group by $t >>> return >>> for $a2 at $pos in $a >>> return <a n="{$pos}">{$a2/text()}</a> >>> }</c> >>> >>> Ciao, >>> M. >>> >>> >>> On 27/01/2017 09:08, meumapple wrote: >>> >>>> Hi, >>>> >>>> I have a structure like this: >>>> >>>> <c> >>>> <a>red</a> >>>> <a>red</a> >>>> <a>blue</a> >>>> <a>green</a> >>>> <a>green</a> >>>> </c> >>>> >>>> And I would like to get: >>>> >>>> <c> >>>> <a n="1">red</a> >>>> <a n="2">red</a> >>>> <a n="1">blue</a> >>>> <a n="1">green</a> >>>> <a n="2">green</a> >>>> </c> >>>> >>>> I have tried with two for loops after having identified the >>>> distinct-values(). This is in principle correct but it takes too long (I >>>> have many distinct values and a elements), because I cannot exit the for >>>> loop as soon as I get a match. I have been able to get the result with the >>>> window clause and then counting the elements of the window with a nested >>>> for-loop: is there an alternative solution? Thanks. >>>> >>>> Joseph >>>> >>> >>> >>> >> >
M
meumapplein reply to Leonard Wörteler, 29 Jan 2017, 13:0329 Jan 2017 22:55 #
Thank you, Leonard. I also thought of the window clause. As for the fold-left, is it a way to express what could also be written as a recursive function?
Quoted text · Leonard Wörteler · 1 line
Il giorno 29 gen 2017, alle ore 13:03, Leonard Wörteler <leonard.woerteler at uni-konstanz.de> ha scritto:
Hi Joseph, if the equal elements are always grouped together, you can use the window clause to gather them into a window and then iterate over that to get the numbers:
   let $src:=
     <c>
       <a>red</a>
       <a>red</a>
       <a>blue</a>
       <a>green</a>
       <a>green</a>
     </c>
   for tumbling window $window in $src/*
       start $s when true()
       end next $n when $s/text() ne $n/text()
   for $elem at $pos in $window
   return <a n="{$pos}">{$elem/node()}</a>
For full control over the state you want to maintain during the iteration, you can use fn:fold-left and pass around a map in which you count how often you've encountered each distinct value:
   let $src:=
     <c>
       <a>red</a>
       <a>red</a>
       <a>blue</a>
       <a>green</a>
       <a>green</a>
     </c>
   return tail(
     fold-left(
       $src/*,
       map{},
       function($acc, $a) {
         let $key := $a/string(),
             $nums := $acc[1],
             $n := ($nums($key), 0)[1] + 1
         return (
           map:merge(($nums, map{ $key: $n })),
           tail($acc),
           <a n="{$n}">{$key}</a>
         )
       }
     )
   )
One element element at a time is added to the accumulator, which consists of the map of counts followed by all elements to be output. Hope that helps, Leo
Quoted text · meumapple · 97 lines
On 29.01.2017 at 09:50, meumapple wrote (with possible deletions): > Thanks to all of you. The group by clause works, while Andy's solution > does not seem to produce the right result on my machine. The group by > clause, however, moves the elements for the grouping. I was wondering > whether it would be possible to count them without moving, and more in > general if there is a way to get more control over 2 for-loops combined, > so that once a condition is satisfied it is possible to stop a certain > repetition. > > Thanks! > Joseph > > Il giorno 27 gen 2017, alle ore 12:33, Andy Bunce <bunce.andy at gmail.com > <mailto:bunce.andy at gmail.com>> ha scritto: > > Hi Joseph, > You could see how XQuery update (http://docs.basex.org/wiki/Update) > performs... > > let $src:=<c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > return copy $result:=$src > modify ( for $a in $result/a > return insert node attribute { 'n' } { > 1+count($a/preceding-sibling::a[.=$a])} into $a) > return $result > > /Andy > > On 27 January 2017 at 11:23, Marco Lettere <m.lettere at gmail.com > <mailto:m.lettere at gmail.com>> wrote: > > Hi Joseph, > what about this? > > let $input := > <c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > return > <c>{ > for $a in $input/* > let $t := $a/text() > group by $t > return > for $a2 at $pos in $a > return <a n="{$pos}">{$a2/text()}</a> > }</c> > > Ciao, > M. > > > On 27/01/2017 09:08, meumapple wrote: > > Hi, > > I have a structure like this: > > <c> > <a>red</a> > <a>red</a> > <a>blue</a> > <a>green</a> > <a>green</a> > </c> > > And I would like to get: > > <c> > <a n="1">red</a> > <a n="2">red</a> > <a n="1">blue</a> > <a n="1">green</a> > <a n="2">green</a> > </c> > > I have tried with two for loops after having identified the > distinct-values(). This is in principle correct but it takes too > long (I have many distinct values and a elements), because I > cannot exit the for loop as soon as I get a match. I have been > able to get the result with the window clause and then counting > the elements of the window with a nested for-loop: is there an > alternative solution? Thanks. > > Joseph
LW
Leonard Wörteler29 Jan 2017 23:24 #
Hi Joseph,
Quoted text · meumapple · 2 lines
On 29.01.2017 at 22:55, meumapple wrote (with possible deletions): > As for the fold-left, is it a way to express what could also be written as a recursive function?
yes it is! fn:fold-left($seq, $acc, $f) can be implemented as the following recursive function:
     declare function local:fold-left($seq, $acc, $f) {
       if(empty($seq)) then $acc
       else local:fold-left(
         tail($seq),
         $f($acc, head($seq)),
         $f
       )
     };
The whole function call can thus be rewritten to the following query with specialized function:
     declare function local:count-elems($seq, $acc) {
       if(empty($seq)) then $acc
       else local:count-elems(
         tail($seq),
         let $key := $seq[1]/string(),
             $nums := $acc[1],
             $n := ($nums($key), 0)[1] + 1
         return (
           map:merge(($nums, map{ $key: $n })),
           tail($acc),
           <a n="{$n}">{$key}</a>
         )
       )
     };
     let $src:=
       <c>
         <a>red</a>
         <a>red</a>
         <a>blue</a>
         <a>green</a>
         <a>green</a>
       </c>
     return tail(
       local:count-elems(
         $src/*,
         map{}
       )
     )
Cheers, Leo

⚡Generated with XQuery