Skip to content

Commit

Permalink
Merge pull request #12 from line-o/next/release
Browse files Browse the repository at this point in the history
next release
  • Loading branch information
line-o committed Jan 17, 2021
2 parents be272eb + 66c28de commit fb3bf8e
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 32 deletions.
24 changes: 22 additions & 2 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

![xBow logo](src/readme.svg)

[![Build Status](https://travis-ci.com/line-o/xbow.svg)](https://travis-ci.com/line-o/xbow)
![Test and Release](https://github.com/line-o/xbow/workflows/Test%20and%20Release/badge.svg) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)

XQuery helper function library to be used with the arrow operator.
Should be read as **crossbow**, a tool to shoot arrows fast and accurately.
Expand Down Expand Up @@ -226,6 +226,22 @@ The above will output an array of 10 anonymous functions.
Each of them comparators suitable to use in `xbow:categorize`.
The range will be from `xbow:lt(-5)` to `xbow:lt(5)`.

`xbow:last` is the latest addition to the utility functions.
It returns the last element of a sequence or array. If the list
does not contain elements an empty sequence is returned.

```xquery
[1, 2, 9] => xbow:last() (: returns 9 :)
```

```xquery
(1, 2, 9) => xbow:last() (: returns 9 :)
```

```xquery
(1, 2, []) => xbow:last() (: returns [] :)
```

## General Arrow Syntax

**Remember:**
Expand Down Expand Up @@ -254,7 +270,7 @@ will throw an **exception**!
## Roadmap / TODOs

- [x] Add convenience functions for boolean tests on sequence items (`all`, `none`, `some`)
- [ ] Rename xbow:map-reverse (to xbow:map-flip for example)
- [x] Rename xbow:map-reverse (to xbow:map-flip for example)
- [ ] Change namespace to `xb` for brevity
- [ ] Test/support elements with namespaces in `wrap*`

Expand All @@ -274,6 +290,10 @@ It depends on functions only available in XQuery version 3.1 (or higher).
The xBow module is compatible with Saxon 10 (HE) since v1.2.0. Older versions of the Home Edition of Saxon
do not allow the use of higher order functions. Saxon 9 PE and 9 EE might work as well.

You can run xBow on baseX (tested with version 9.4.5). To install the most recent version run
`REPO INSTALL https://raw.githubusercontent.com/line-o/xbow/master/src/content/xbow.xqm`
in basex REPL.

The released package requires eXist-db 4.7.0 or higher, due to a bug that caused
unpredictable behaviour when using `xbow:wrap-element` and related functions (see [original issue](https://github.com/eXist-db/exist/issues/1960) for details).

Expand Down
86 changes: 64 additions & 22 deletions src/content/xbow.xqm
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ function xbow:pluck-node ($node as node(), $field as xs:string+) as item()* {

declare
%private
function xbow:pluck-node-part ($node as node(), $path-part as xs:string?) {
function xbow:pluck-node-part ($node as node(), $path-part as xs:string?) as node()? {
if (contains($path-part, ':') or contains($path-part, '[') or contains($path-part, '('))
then (error(
xs:QName('xbow:invalid-dynamic-path'),
Expand Down Expand Up @@ -255,7 +255,7 @@ function xbow:sequence-stats-reducer ($result as map(*), $next as xs:numeric) as
declare variable $xbow:initial-stats :=
map { 'min': (), 'max': (), 'avg': 0.0, 'sum': 0, 'length': 0 };

declare function xbow:num-stats ($sequence as xs:numeric*) {
declare function xbow:num-stats ($sequence as xs:numeric*) as map(*) {
fold-left($sequence, $xbow:initial-stats, xbow:sequence-stats-reducer#2)
};

Expand Down Expand Up @@ -395,13 +395,16 @@ function xbow:map-filter-keys ($map as map(*), $keys as xs:string*) {
};

(:~
: reverse keys and values of a map
: flip keys and values of a map
: Example:
xbow:map-reverse(map { 'key': 'value'})
:)
declare
function xbow:map-reverse ($map as map(*)) {
map:for-each($map, function ($k, $v) { map { $v: $k } })
function xbow:map-flip ($map as map(*)) as map(*) {
map:for-each($map,
function ($key as xs:anyAtomicType, $value as xs:anyAtomicType) as map(*) {
map { $value : $key }
})
=> map:merge()
};

Expand All @@ -419,10 +422,10 @@ function xbow:map-reverse ($map as map(*)) {
xbow:map-reverse(map { 'key': 'value'}, function ($v) { util:uuid($v) })
:)
declare
function xbow:map-reverse ($map as map(*), $hash-value as function(*)) {
function xbow:map-flip ($map as map(*), $hash-value as function(*)) as map(*) {
map:for-each($map,
function ($k, $v) {
map { $hash-value($v): $k }
function ($key as xs:anyAtomicType, $value as item()*) as map(*) {
map { $hash-value($value): $key }
})
=> map:merge()
};
Expand Down Expand Up @@ -492,7 +495,7 @@ function xbow:categorize ($sequence as item()*, $rules as array(function(*))) as
: spread all elements of $sequence over $scale using an $accessor function
:)
declare
function xbow:categorize ($sequence as item()*, $rules as array(function(*)), $accessor as function(*)) as function(*) {
function xbow:categorize ($sequence as item()*, $rules as array(function(*)), $accessor as function(*)) as array(*) {
let $test := xbow:find-first-matching($rules, ?)
let $zero := xbow:array-fill(array:size($rules), ())

Expand Down Expand Up @@ -534,6 +537,41 @@ function xbow:array-put ($array as array(*), $pos as xs:integer, $items-to-put a
))
};

(:~
: Return the last item in a sequence
: will return an empty array if array is empty
:)
declare
function xbow:last($array-or-sequence as item()*) as item()? {
typeswitch($array-or-sequence)
case array(*)
return xbow:last-member-of($array-or-sequence)
default
return xbow:last-item-of($array-or-sequence)
};

(:~
: Return the last item in a sequence
: will return an empty array if array is empty
:)
declare
function xbow:last-item-of($seq as item()*) as item()? {
if (count($seq))
then ($seq[count($seq)])
else ()
};

(:~
: Return the last item in array (guarded)
: will return an empty array if array is empty
:)
declare
function xbow:last-member-of($array as array(*)) as item()? {
if (array:size($array))
then ($array(array:size($array)))
else ()
};

(:~
: Extended for-each, will pass the position of the current item
: to the provided function.
Expand All @@ -543,7 +581,7 @@ function xbow:array-put ($array as array(*), $pos as xs:integer, $items-to-put a
xbow:sequence-for-each-index((1,2,3), function ($i, $p) { $i + $p })
:)
declare
function xbow:sequence-for-each-index ($seq as item()*, $func as function (*)) as item()* {
function xbow:sequence-for-each-index ($seq as item()*, $func as function(*)) as item()* {
fold-left($seq, [0, ()], function ($result as array(*), $next as item()) {
let $pos := $result?1 + 1
return [$pos, ($result?2, $func($next, $pos))]
Expand Down Expand Up @@ -603,33 +641,34 @@ function xbow:array-fill ($size as xs:integer, $function-or-value as item()?) as
return xbow:array-for-each-index(
array { (1 to $size) }, $function-or-value)
default
(: we need an array to iterate over for [(),()] to be possible :)
return array:for-each(
array { (1 to $size) }, function ($ignore) { $function-or-value })
)
};

declare
%private
function xbow:match-first ($item, $result, $match) {
function xbow:match-first ($item as item(), $result as xs:integer+, $rule as function(*)) as xs:integer+ {
let $current-pos := head($result) + 1
let $current-match := tail($result)

return
if ($current-match > 0)
then $result (: did match, do nothing :)
else if ($match($item))
else if ($rule($item))
then ($current-pos, $current-pos)
else ($current-pos, $current-match)
};

declare
%private
function xbow:match-all ($item, $result, $match) {
function xbow:match-all ($item as item(), $result as xs:integer+, $rule as function(*)) as xs:integer+ {
let $current-pos := head($result) + 1
let $current-match := tail($result)

return
if ($match($item))
if ($rule($item))
then ($current-pos, $current-pos)
else ($current-pos, $current-match)
};
Expand All @@ -646,14 +685,17 @@ function xbow:find-first-matching ($rules as array(*), $item as item()) as xs:in
: with two keys each:
: 'items' (n-th item of the first array) and
: 'label' (n-th item of the second array)
: NOTE: both arrays must be of same length
: NOTE: both arrays must have the same size
:)
declare
function xbow:label($array as array(*), $labels as array(*)) as array(map(*)) {
array:for-each-pair($array, $labels, function ($items, $label) {
map {
'items': $items,
'label': $label
}
})
function xbow:label ($array as array(*), $labels as array(*)) as array(map(*)) {
array:for-each-pair($array, $labels, xbow:assign-label#2)
};

declare %private
function xbow:assign-label ($items as item()*, $label as xs:string) as map(xs:string, item()*) {
map {
'items': $items,
'label': $label
}
};
84 changes: 76 additions & 8 deletions src/test/xbow-spec.xqm
Original file line number Diff line number Diff line change
Expand Up @@ -368,35 +368,35 @@ function xbow-spec:ascending () {

declare
%test:assertEquals('-1', '0', '1', '8', '9', '10', '11', '16', '31')
function xbow-spec:map-reverse () {
function xbow-spec:map-flip () {
$xbow-spec:map
=> xbow:map-reverse()
=> xbow:map-flip()
=> map:keys()
=> xbow:ascending()
};

declare
%test:assertEquals('-2', '-1', '0', '7', '8', '9', '10', '15', '30')
function xbow-spec:map-reverse-function-add () {
function xbow-spec:map-flip-function-add () {
$xbow-spec:map
=> xbow:map-reverse(function ($k) {xs:int($k) - 1})
=> xbow:map-flip(function ($k) {xs:int($k) - 1})
=> map:keys()
=> xbow:ascending()
};

declare
%test:assertEquals('1f61f08a-04d5-3a9b-a749-7fad4d9b612c')
function xbow-spec:map-reverse-function-uuid () {
function xbow-spec:map-flip-function-uuid () {
map { 'key': 'value' }
=> xbow:map-reverse(util:uuid(?))
=> xbow:map-flip(util:uuid(?))
=> map:keys()
};

declare
%test:assertEquals('55')
function xbow-spec:map-reverse-function-sum () {
function xbow-spec:map-flip-function-sum () {
map { 'key': (1 to 10) }
=> xbow:map-reverse(sum(?))
=> xbow:map-flip(sum(?))
=> map:keys()
};

Expand Down Expand Up @@ -555,3 +555,71 @@ function xbow-spec:combine-for-each () {

return $r1 = $r2
};

declare
%test:assertFalse
function xbow-spec:last-member-of-empty () {
[]
=> xbow:last-member-of()
=> exists()
};

declare
%test:assertEquals(9)
function xbow-spec:last-member-of-range () {
array { (1 to 9) }
=> xbow:last-member-of()
};

declare
%test:assertFalse
function xbow-spec:last-item-of-empty () {
()
=> xbow:last-item-of()
=> exists()
};

declare
%test:assertEquals(9)
function xbow-spec:last-item-of-range () {
(1 to 9)
=> xbow:last-item-of()
};

declare
%test:assertFalse
function xbow-spec:last-empty () {
()
=> xbow:last()
=> exists()
};

declare
%test:assertFalse
function xbow-spec:last-empty-array () {
[]
=> xbow:last()
=> exists()
};

declare
%test:assertEquals(9)
function xbow-spec:last-array () {
array { (1 to 9) }
=> xbow:last()
};

declare
%test:assertEquals(9)
function xbow-spec:last-sequence () {
(1 to 9)
=> xbow:last()
};

declare
%test:assertEquals(1)
function xbow-spec:last-item-is-array () {
(1 to 9, [1])
=> xbow:last()
=> array:get(1)
};

0 comments on commit fb3bf8e

Please sign in to comment.