Last active
October 17, 2022 16:46
-
-
Save joewiz/228e9cc174694e146cc8 to your computer and use it in GitHub Desktop.
Convert Roman numerals to integers, with XQuery, https://joewiz.org/2021/05/30/converting-roman-numerals-with-xquery-xslt/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
xquery version "3.0"; | |
module namespace r = "http://joewiz.org/ns/xquery/roman-numerals"; | |
(: Converts standard Roman numerals to integers. | |
: Handles additive and subtractive but not double subtractive. | |
: Case insensitive. | |
: Doesn't attempt to validate a numeral other than a naïve character check. | |
: See discussion of standard modern Roman numerals at: | |
: http://en.wikipedia.org/wiki/Roman_numerals. | |
: Adapted from an XQuery 1.0 module at: | |
: https://github.com/subugoe/ropen-backend/blob/master/src/main/xquery/queries/modules/roman-numerals.xqm | |
: Credit to Mattio Valentino: | |
: https://twitter.com/joewiz/status/1398359600257290242 | |
:) | |
declare function r:decode-roman-numeral($input as xs:string) as xs:integer { | |
let $characters := string-to-codepoints(upper-case($input)) ! codepoints-to-string(.) | |
let $character-to-integer := | |
function($character as xs:string) { | |
switch ($character) | |
case "I" return 1 | |
case "V" return 5 | |
case "X" return 10 | |
case "L" return 50 | |
case "C" return 100 | |
case "D" return 500 | |
case "M" return 1000 | |
default return error(xs:QName('roman-numeral-error'), concat('Invalid input: ', $input, '. Valid Roman numeral characters are I, V, X, L, C, D, and M. This function is case insensitive.')) | |
} | |
let $numbers := $characters ! $character-to-integer(.) | |
let $values := | |
for $number at $n in $numbers | |
return | |
if ($number < $numbers[position() = $n + 1]) then | |
-$number (: Handles subtractive notation of Roman numerals. :) | |
else | |
$number | |
return | |
sum($values) | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
xquery version "3.0"; | |
(: Converts Roman numerals to integers. | |
: Pre-computes Roman numerals for 1-3999. | |
: Inspired by David Sewell's tweet: | |
: https://twitter.com/DavidSewellVA/status/1399036814736953351 | |
:) | |
module namespace r = "http://joewiz.org/ns/xquery/roman-numerals"; | |
declare variable $r:roman-numerals := (1 to 3999) ! format-integer(., "I"); | |
declare function r:decode-roman-numeral($roman-numeral as xs:string) { | |
index-of($r:roman-numerals, upper-case($roman-numeral)) | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
xquery version "3.1"; | |
module namespace r = "http://joewiz.org/ns/xquery/roman-numerals"; | |
(: Converts Roman numerals to integers. | |
: Adapted from Clojure version at: | |
: https://github.com/OpenRefine/OpenRefine/wiki/Recipes#convert-roman-numerals-to-arabic | |
: Written up at: | |
: https://joewiz.org/2021/05/30/converting-roman-numerals-with-xquery-xslt/ | |
: See also: | |
: https://rosettacode.org/wiki/Roman_numerals/Decode | |
:) | |
declare function r:decode-roman-numeral($roman-numeral as xs:string) as xs:integer { | |
$roman-numeral | |
=> upper-case() | |
=> for-each( | |
function($roman-numeral-uppercase) { | |
analyze-string($roman-numeral-uppercase, ".")/fn:match/string() | |
} | |
) | |
=> for-each( | |
map { "M": 1000, "D": 500, "C": 100, "L": 50, "X": 10, "V": 5, "I": 1 } | |
) | |
=> fold-right( [0, 0], | |
function($number as xs:integer, $accumulator as array(*)) { | |
let $running-total := $accumulator?1 | |
let $previous-number := $accumulator?2 | |
return | |
if ($number lt $previous-number) then | |
[ $running-total - $number, $number ] | |
else | |
[ $running-total + $number, $number ] | |
} | |
) | |
=> array:head() | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment