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
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
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@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
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@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@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
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@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@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@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
The result is not the one I look for
Il giorno 29 gen 2017, alle ore 12:58, Andy Bunce bunce.andy@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@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@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@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
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.htm... ) 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
On 29 January 2017 at 13:37, meumapple meumapple@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@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@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@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@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
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
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@gmail.com mailto:bunce.andy@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@gmail.com mailto:m.lettere@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
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?
Il giorno 29 gen 2017, alle ore 13:03, Leonard Wörteler leonard.woerteler@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
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@gmail.com mailto:bunce.andy@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@gmail.com mailto:m.lettere@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
Hi Joseph,
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
basex-talk@mailman.uni-konstanz.de