{"id":15494112,"url":"https://github.com/line-o/xbow","last_synced_at":"2026-01-08T03:13:04.439Z","repository":{"id":38173683,"uuid":"166060095","full_name":"line-o/xbow","owner":"line-o","description":"Shooting arrows fast and accurately","archived":false,"fork":false,"pushed_at":"2023-01-24T00:44:09.000Z","size":861,"stargazers_count":13,"open_issues_count":12,"forks_count":1,"subscribers_count":7,"default_branch":"main","last_synced_at":"2024-10-19T11:31:09.829Z","etag":null,"topics":["basex","exist-db","library","saxon","xquery"],"latest_commit_sha":null,"homepage":"","language":"XQuery","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/line-o.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-16T15:08:25.000Z","updated_at":"2023-12-03T09:32:33.000Z","dependencies_parsed_at":"2023-02-13T06:10:12.241Z","dependency_job_id":null,"html_url":"https://github.com/line-o/xbow","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/line-o%2Fxbow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/line-o%2Fxbow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/line-o%2Fxbow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/line-o%2Fxbow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/line-o","download_url":"https://codeload.github.com/line-o/xbow/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246068302,"owners_count":20718503,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["basex","exist-db","library","saxon","xquery"],"created_at":"2024-10-02T08:11:26.514Z","updated_at":"2026-01-08T03:13:04.386Z","avatar_url":"https://github.com/line-o.png","language":"XQuery","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xBow\n\n![xBow logo](src/readme.svg)\n\n![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)\n\nXQuery helper function library to be used with the arrow operator.\nShould be read as **crossbow**, a tool to shoot arrows fast and accurately.\n\n## Usage\n\nThe library provides a small number of useful functions when working with the\narrow operator with sequences or sequences in general.\n\nInstall the [XAR](https://github.com/line-o/xbow/releases/) and\n\n    import module namespace xbow=\"http://line-o.de/xq/xbow\";\n\nBelow you'll find some key things xbow has to offer.\nFor more, have a look at the\n[examples](https://github.com/line-o/xbow/tree/master/src/examples) and\n[tests](https://github.com/line-o/xbow/tree/master/src/test).\n\nEven if the module was developed with arrow expressions in mind,\nall of it can be used without this language feature, as well.\n\n## Requirements\n\n- XQuery version 3.1+\n- eXist-db version 4.7.0+\n\n## Accessors\n\nMany of the functions in this module have a second signature, expecting an accessor function.\nThis concept was inspired by [D3](https://d3js.org), since it is an excellent tool to mangle data.\n\nThe idea is that you tell the operation you want to perform how to retrieve the value it should \noperate on. \nMaybe you want to filter user elements by let's sey their karma points property, but you do not want to lose\nthe elements itself in the process. Then, passing an accessor to the comparison function would\ndo the trick.\nSince the key function (or accessor) knows how to deal with the datatype of your\nsequence items, most operations can be used on atomics, nodes, maps and arrays. \nAll `item()*` welcome.\n\nIn the section [Grouping](#Grouping) is a code example.\n\nThere are two functions, `xbow:pluck` and `xbow:pluck-deep`, that can help making use of accessors.\nThe second example in [Filtering](#Filtering) uses pluck.\n\n## Filtering\n\n```xquery\n(0 to 9) =\u003e filter(xbow:gt(4)) =\u003e filter(xbow:lt(6))\n```\n\nThe above code outputs 5 since all numbers less and greater were filtered out.\n\n`xbow` implements the usual comparison functions `eq`, `ne`, `lt`, `le`, `gt` and `ge`.\n\nThey all return a function, so that they can be used in combination with filter and\nfor-each. They all accept an [accessor](#Accessors) as a second argument.\n\nHere it is in action (this time using an array as input):\n\n```xquery\n[\n  map { 'a': 1, 'b': 2 },\n  map { 'a': 2, 'b': 1 },\n  map { 'a': 3, 'b': 1 }\n] \n    =\u003e array:filter(xbow:eq(1, xbow:pluck('b')))\n    =\u003e array:for-each(xbow:pluck('a'))\n    (: yields (2, 3) :)\n```\n\n## Grouping\n\nGrouping items after an arrow would require some boilerplate code.\nThis is where `xbow:groupBy` comes in handy.\n\n```xquery\n(\n  \u003cuser first='Mike' last='Hill'/\u003e,\n  \u003cuser first='Paula' last='Moon'/\u003e,\n  \u003cuser first='Carla' last='Harlowe'/\u003e,\n  \u003cuser first='Fela' last='Kuti'/\u003e\n)\n  =\u003e xbow:groupBy(function ($item as element()) {\n      substring($item/@last, 1, 1) })\n```\n\nThe second parameter is an accessor function. A concept shamelessly copied from [D3](https://d3js.org).\n\n\nThe function will always return a map. It will have all items of the input sequence.\nAny value produced by the accessor function, will be a key in the resulting map.\n\n```xquery\nmap {\n  'H': (\n    \u003cuser first='Mike' last='Hill'/\u003e,\n    \u003cuser first='Carla' last='Harlowe'/\u003e\n  )\n  'M': \u003cuser first='Paula' last='Moon'/\u003e,\n  'K': \u003cuser first='Fela' last='Kuti'/\u003e\n}\n```\n\n## Sorting\n\nThis is just a small wrapper around the normal sort function.\nMainly, to not have to remember to add an empty sequence,\nif you just want to use a sorting function.\n\nThis will sort numerical entries in descending order.\n\n```xquery\n(0, 3, 9, 8) =\u003e xbow:descending()\n```\n\nReturns `(9, 8, 3, 0)`\n\n\nUsing `fn:sort` produces the same output.\n\n```xquery\n(0, 3, 9, 8) =\u003e sort((), function ($a) { -$a })\n```\n\n## Folding\n\n`fold-left`, `fold-right`\n\nSometimes you want to test a sequence of items if all, some or\nnone of them meet a certain condition.\n\n### All\n\n`xbow:all` returns true, if the testing function returns `true()` for _each item_.\n\n```xquery\n(1 to 4) =\u003e xbow:all(xbow:lt(5))\n```\n\n### Some\n\n`xbow:some` returns true, if the testing function returns `true()` for _at least one item_.\n\n```xquery\n('1', '2', '3') =\u003e xbow:some(xbow:eq('2'))\n```\n\n### None\n\n`xbow:none` returns true, if the testing function returns `false()` for _each item_.\n\n```xquery\n(['1', '1'], ['1', '2'], ['1', '3']) =\u003e xbow:none(xbow:eq('2', xbow:pluck(1)))\n```\n\n## Nodes\n\nThere is a number of functions to output or operate on nodes, attributes and elements.\n\nFor example:\nWrapping a single value in an element of a certain type with `wrap-element` or\neach item in a sequence (`wrap-each`).\n\n```xquery\n(1 to 3) \n  =\u003e xbow:wrap-each('item')\n  =\u003e xbow:wrap-element('root')\n```\n\noutputs\n\n```xml\n\u003croot\u003e\n  \u003citem\u003e1\u003c/item\u003e\n  \u003citem\u003e2\u003c/item\u003e\n  \u003citem\u003e3\u003c/item\u003e\n\u003c/root\u003e\n```\n\n## Utility\n\nSome handy expressions in XQuery, like `?*` to convert an array\ninto a sequence, cannot be easily used after the arrow operator.\nxbow wraps those in functions for you so that you do not have to.\n\n```xquery\n[1,2] =\u003e xbow:to-sequence(),\n(1,2) =\u003e xbow:to-array()\n```\n\nFLWOR operations allow you to operate on the position of each element.\nSince `fn:for-each` does not allow that, `xbow:for-each-index` was added.\nIt can operate on a sequence or an array.\n\n```xquery\n(2 to 4)\n  =\u003e xbow:for-each-index(function ($v, $i) {\n    if ($i = 2) then ($v - $i) else ($v)\n  })\n```\n\nresults in `(2, 1, 4)`. Only the value for the second item changed.\n\n\n`xbow:array-fill` lets you create an arrayfill an array with a value\n\n```xquery\nxbow:array-fill(3, true())\n```\n\nor the return value of a function\n\n```xquery\nxbow:array-fill(10, function ($value, $position) {\n  xbow:lt(($value - 5))\n})\n```\n\nThe above will output an array of 10 anonymous functions.\nEach of them comparators suitable to use in `xbow:categorize`.\nThe range will be from `xbow:lt(-5)` to `xbow:lt(5)`.\n\n`xbow:last` is the latest addition to the utility functions.\nIt returns the last element of a sequence or array. If the list\ndoes not contain elements an empty sequence is returned.\n\n```xquery\n[1, 2, 9] =\u003e xbow:last() (: returns 9 :)\n```\n\n```xquery\n(1, 2, 9) =\u003e xbow:last() (: returns 9 :)\n```\n\n```xquery\n(1, 2, []) =\u003e xbow:last() (: returns [] :)\n```\n\n`xbow:get-type` will return type information of the provided item (not a sequence).\n\nNOTE: It will look deep into the inner structure of the item, if it is nested.\nSequences as well as arities are not detected. Whenever mixed content is found \"*\" is returned.\n\n```xquery\n1 =\u003e xbow:get-type() (: returns \"xs:integer\" :)\n```\n\n```xquery\n[1, 2, true()] =\u003e xbow:get-type() (: returns \"array(*)\" :)\n```\n\n```xquery\n[map {1: \"one\", 2: \"two\", 9:\"nine\"}, map {1: \"eins\", 2: \"zwei\", 9:\"neun\"}] \n  =\u003e xbow:get-type() (: returns \"array(map(xs:integer, xs:string))\" :)\n```\n\n## General Arrow Syntax\n\n**Remember:**\n\nThe arrow operator _must_ be followed by a function expression.\nThe first argument _will_ be the left hand side, there is no way around that.\n\n```xquery\n(0 to 9) =\u003e sum(),\n(0 to 9) =\u003e (function($a) { sum($a) })()\n```\nis **fine**.\n\n```xquery\n(0 to 9) =\u003e concat(?)\n```\nwill return **a function** with an arity of 1.\n\n**But**\n\n`(0 to 9) =\u003e sum(.)`,\n\n`(0 to 9) =\u003e sum#1` or\n\n`(0 to 9) =\u003e function ($a) { sum($a) }`\n\nwill throw an **exception**!\n\n## Roadmap / TODOs\n\n- [x] Add convenience functions for boolean tests on sequence items (`all`, `none`, `some`) \n- [x] Rename xbow:map-reverse (to xbow:map-flip for example)\n- [ ] Change namespace to `xb` for brevity\n- [ ] Test/support elements with namespaces in `wrap*`\n\n- [ ] More examples\n- [ ] Extend documentation on concepts\n- [ ] Generate XQDoc documentation at build\n\n- [x] Replace `ant` with `gulp-exist` +watcher \n- [ ] ~~Create packages for other XQuery runtimes~~\n- [ ] Split up into modules with separate scopes (DOM, utility, ...)\n- [ ] Add element constructors (inspired by Micheal Kays proposal)\n- [ ] Rename `xbow:groupBy` to `xbow:group-by` to adhere to the XQuery naming convetions\n\n## Compatibility\n\nThe xBow module tests are written for [eXist-db](https://exist-db.org) and this is also its build target.\nIn theory the core library module should run on any XQuery 3.1 processor.\nIt depends on functions only available in XQuery version 3.1 (or higher).\n\nThe xBow module is compatible with Saxon 10 (HE) since v1.2.0. Older versions of the Home Edition of Saxon\ndo not allow the use of higher order functions. Saxon 9 PE and 9 EE might work as well. \n\nYou can run xBow on baseX (tested with version 9.4.5). To install the most recent version run\n`REPO INSTALL https://raw.githubusercontent.com/line-o/xbow/master/src/content/xbow.xqm`\nin basex REPL.\n\nThe released package requires eXist-db 4.7.0 or higher, due to a bug that caused\nunpredictable behaviour when using `xbow:wrap-element` and related functions (see [original issue](https://github.com/eXist-db/exist/issues/1960) for details).\n\n## Performance\n\nWe are trading readability, maintainability and comfort for speed with this module.\nFLOWR-expressions are well optimized and execute at least twice as fast.\nThis module will only really shine when the runtime is able to parallelize function calls and would\nalso benefit heavily from lazy evaluation.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fline-o%2Fxbow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fline-o%2Fxbow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fline-o%2Fxbow/lists"}