jq to report rolling differences between array element values - json

I have an input like below which just has stageIds, along with their submit and completion time in unix time seconds
[
{
"stageId": 1,
"submitTime_epoch_secs": 5,
"completionTime_epoch_secs": 10
},
{
"stageId": 2,
"submitTime_epoch_secs": 15,
"completionTime_epoch_secs": 17
},
{
"stageId": 3,
"submitTime_epoch_secs": 29,
"completionTime_epoch_secs": 30
}
]
desired output is below, where each stageId, submit, and completion times are compared with previous and next and the delay is added as another key/val per element.
[
{
"stageId": 1,
"submitTime_epoch_secs": 5,
"completionTime_epoch_secs": 10,
"delayTillNextStageSubmit",5
"delayFromPrevStageComplete",null
},
{
"stageId": 2,
"submitTime_epoch_secs": 15,
"completionTime_epoch_secs": 17,
"delayTillNextStageSubmit",12
"delayFromPrevStageComplete",5
},
{
"stageId": 3,
"submitTime_epoch_secs": 29,
"completionTime_epoch_secs": 30,
"delayTillNextStageSubmit",null
"delayFromPrevStageComplete",12
}
]
here the stageId 1 delayTillNextStageSubmit is difference between stageId 2 submitTime and stageId 1 completion time, 15 - 10 = 5.
is this possible with jq?
I am new to jq, so don't know how to solve this

Here is a derivative method for this kind of task:
# Input: an array
# $delta is the lag (e.g. 1) or lead (e.g. -1)
# p1 is the jq path expression to the quantity within "this" item
# p2 is the jq path expression to the quantity within "that" item
# q is the jq path expression for the delta value
def derivative($delta; p1; p2; q):
. as $in
| length as $length
| [ range(0; $length) as $i
| .[$i]
| if 0 <= $i and $i < $length and 0 <= ($i - $delta) and ($i - $delta) < $length
then q = ($in[$i] | p1) - ($in[$i - $delta] | p2)
| q |= (if $delta > 0 then . else - . end)
else q = null
end ];
If $data holds your data, then based on my understanding of the question, you would invoke it like this:
$data
| derivative(-1; .completionTime_epoch_secs; .submitTime_epoch_secs; .delayTillNextStageSubmit)
| derivative( 1; .submitTime_epoch_secs; .completionTime_epoch_secs; .delayFromPrevStageComplete)
Please note there seems to be an inconsistency between the input and output for stageId 3 in your example: "submitTime_epoch_secs" is shown as 22 and then as 29.

. as $array | to_entries | map(
.value.delayFromPrevStageComplete = .value.submitTime_epoch_secs - $array[.key - 1].completionTime_epoch_secs |
.value.delayTillNextStageSubmit = $array[(.key + 1) % length].submitTime_epoch_secs - .value.completionTime_epoch_secs |
.value
) | (
.[0].delayFromPrevStageComplete,
.[-1].delayTillNextStageSubmit
) = null
to_entries is used to get the index of each entry as .key.
(.key + 1) % length is used to avoid an error with the last element.

Related

Calculate accumulated times from json with bash script

I have data in json format that logs timestamps (hh:mm in 24h format) with an event (In/Out). My goal is to add up all the time differences between an "IN" event and the next "OUT" event.
For simplification I assume that there are no inconsistencies (The first element is always an "IN" and each "IN" is followed by an "OUT"). Exception: If the last element is an "IN", the calculation has to be done between the current time and the timestamp from the last "IN" event.
This is my script so far, which calculates all the timespans, also between an OUT and an IN event. But I need only those that are inbetween an IN and OUT event.
Any tips what might be more useful here are welcome !
#!/bin/bash
JSON='{ "times": [ [ "7:43", "IN" ], [ "8:26", "OUT" ], [ "8:27", "IN" ], [ "9:12", "OUT" ], [ "9:14", "IN" ], [ "9:22", "OUT" ], [ "9:23", "IN " ], [ "12:12", "OUT" ], [ "13:12", "IN" ] ]}'
IN_TIMES=$(jq '.times | to_entries | .[] | select(.value[1]| tostring | contains("IN")) | .value[0]' <<< "$JSON")
OUT_TIMES=$(jq '.times | to_entries | .[] | select(.value[1]| tostring | contains("OUT")) | .value[0]' <<< "$JSON")
ALL_TIMES=$(jq -r '.times| to_entries | .[] | .value[0]' <<< "$JSON")
prevtime=0
count=0
for i in $(echo $ALL_TIMES | sed "s/ / /g")
do
if [[ "$count" -eq 0 ]]; then
(( count++ ))
prevtime=$i
continue
else
(( count++ ))
fi
time1=`date +%s -d ${prevtime}`
time2=`date +%s -d ${i}`
diffsec=`expr ${time2} - ${time1}`
echo From $prevtime to $i: `date +%H:%M -ud #${diffsec}`
prevtime=$i
done
Here's an only-jq solution that only calls jq once.
Please note, though, that it may need tweaking to take into account time zone considerations, error-handling, and potentially other complications:
def mins: split(":") | map(tonumber) | .[0] * 60 + .[1];
def diff: (.[1] - .[0]) | if . >= 0 then . else 24*60 + . end;
def now_mins: now | gmtime | .[3] * 60 + .[4];
def pairs:
range(0; length; 2) as $i | [.[$i], .[$i+1] ];
def sigma(s): reduce s as $s (0; . + $s);
.times
| map( .[0] |= mins )
| if .[-1][1] == "IN" then . + [ [now_mins, "OUT"] ] else . end
| sigma(pairs | map(.[0]) | diff)
Since you measure times up to the minute, it is enough to compute minutes without messing up with the command date. I have an awk solution:
awk -F: -vIRS=" " -vfmt="From %5s to %5s: %4u minutes\n" \
'{this=$1*60+$2}a{printf(fmt,at,$0,this-a);a=0;next}{a=this;at=$0}\
END{if(a){$0=strftime("%H:%M");printf(fmt,at,$0,$1*60+$2-a)}}' <<<"$ALL_TIMES"
which works by defining a colon as field separator and a space as record separator. In this way we get a separate record with two fields for each time. Then
{this=$1*60+$2} : We compute how many minutes there are in the current record and put them in the variable this.
a{printf(fmt,at,$0,this-a);a=0;next} : If the (initially empty) variable a is not null nor zero, we are reading an OUT entry, so we print what we want, set a to zero because the next field will be an IN entry, and we continue to the next record.
{a=this;at=$0} : Otherwise, we are reading an IN entry, and set a to its minutes and at to its string representation (needed we will print it, as per previous case).
END{if(a){$0=strftime("%H:%M");printf(fmt,at,$0,$1*60+$2-a)}} : at the end, if we still have some dangling IN data, we set $0 to be the properly formatted current time and print what we want.
All done.
With Xidel and a little XQuery magic this is rather simple:
#!/bin/bash
JSON='{"times": [["7:43", "IN"], ["8:26", "OUT"], ["8:27", "IN"], ["9:12", "OUT"], ["9:14", "IN"], ["9:22", "OUT"], ["9:23", "IN "], ["12:12", "OUT"], ["13:12", "IN"]]}'
xidel -s - --xquery '
let $in:=$json/(times)()[contains(.,"IN")](1) ! time(
substring(
"00:00:00",
1,
8-string-length(.)
)||.
),
$out:=$json/(times)()[contains(.,"OUT")](1) ! time(
substring(
"00:00:00",
1,
8-string-length(.)
)||.
)
for $x at $i in $out return
concat(
"From ",
$in[$i],
" to ",
$x,
": ",
$x - $in[$i] + time("00:00:00")
)
' <<< "$JSON"
$in:
00:07:43
00:08:27
00:09:14
00:09:23
00:13:12
$out:
00:08:26
00:09:12
00:09:22
00:12:12
Output:
From 00:07:43 to 00:08:26: 00:00:43
From 00:08:27 to 00:09:12: 00:00:45
From 00:09:14 to 00:09:22: 00:00:08
From 00:09:23 to 00:12:12: 00:02:49

Spread number equally across elements of array (and add remainder to beginning of ring)

Let's say I have some JSON array, we'll call it A:
["foo", "bar", "baz"]
And I have some number X, let's say 5 in this case.
I want to produce the following object in jq:
{
"foo": 2,
"bar": 2,
"baz": 1,
}
This is the number 5 divided up equally across the elements of the array, with the remainder being distributed to the elements at the beginning of the ring. You could maybe think of it this way, the value for element N should be ceil(X / length(A)) if index(N) < (X % length(A)), otherwise it should be floor(X / length(A)).
Assuming A is my file input to jq, and I have X defined as a variable, how can I express this in jq?
I have tried 'length as $len | .[] | if index(.) < (5 % $len) then (5 / $len) + 1 else 5 / $len end' | 5 as a starting point but I get 2 for each element.
You can use the transpose function to help build this. It's simpler with a ceil function, which we have to define ourselves. The mapping you are looking for from index to allocation is ceil($count - $i)/$n), where $count is the amount you are distributing, $i is the index in the original list, and $n is the length of the list.
Comments show how each piece works on your sample input of ["foo", "bar", "baz"].
def ceil(v): -(-v | floor);
def objectify(n): {key: .[0], value: ceil(($count - .[1])/n)};
# ["foo", 0] | objectify(3) -> {"key": "foo", "value", 2}
length as $n | # n == 3
[., keys] | # [["foo", "bar", "baz"], [0,1,2]]
[transpose[] | # [["foo", 0], ["bar", 1], ["baz", 2]]
objectify($n)
] |
from_entries # {"foo": 2, "bar": 2, "baz": 1}
Without the comments...
def ceil(v): -(-v | floor);
def objectify(n): {key: .[0], value: ceil(($count - .[1])/n)};
length as $n | [., keys] | [transpose[] | objectify($n)] | from_entries
An example of its use, assuming you saved it to file named distribute.jq:
jq --argjson count 5 -f distribute.jq tmp.json
I found a solution by saving the original input as a variable so that I can continue to reference it while operating on its values.
. as $arr
| length as $len
| [
.[]
| . as $i
| {
$(i): (
if ($arr | index($i)) < ($x % $len) then
($x / $len) + 1
else
$x / $len
end
| floor
)
}
]
| add
The following worked for me with passing --argjson count $X and feeding the array as my input.

Piping an empty object has a count of 1

I can't seem to get this function quite right. I want to pass it an object and if the object is empty, return 1, else count items in the object and increment by 1.
Assuming the following function "New-Test":
function New-Test
{
[cmdletbinding()]
Param
(
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[object[]]$Object
#[object]$object
)
Begin
{
$oData=#()
}
Process
{
"Total objects: $($object.count)"
if($Object.count -gt 0)
{
$oData += [pscustomobject]#{
Name = $_.Name
Value = $_.Value
}
}
Else
{
Write-Verbose "No existing object to increment. Assuming first entry."
$oData = [pscustomobject]#{Value = 0}
}
}
End
{
$LatestName = ($oData | Sort-Object -Descending -Property Value | Select -First 1).value
[int]$intNum = [convert]::ToInt32($LatestName, 10)
$NextNumber = "{0:00}" -f ($intNum+1)
$NextNumber
}
}
And the following test hashtable:
#Create test hashtable:
$a = 00..08
$obj = #()
$a | foreach-object{
$obj +=[pscustomobject]#{
Name = "TestSting" + "{0:00}" -f $_
Value = "{0:00}" -f $_
}
}
As per the function above, if I pass it $Obj, I get:
$obj | New-Test -Verbose
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
09
Which is as expected. However, if I pass it $Obj2:
#Create empty hash
$obj2 = $null
$obj2 = #{}
$obj2 | New-Test -Verbose
I get:
Total objects: 1
Exception calling "ToInt32" with "2" argument(s): "Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: startIndex"
At line:33 char:9
+ [int]$intNum = [convert]::ToInt32($LatestName, 10)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentOutOfRangeException
01
I don't understand why $object.count is 1, when there's nothing in the hashtable.
If I change the parameter, $object's type from [object[]] to [object], the empty hashtable test results in:
$obj2 | New-Test -Verbose
Total objects: 0
VERBOSE: No existing object to increment. Assuming first entry.
01
Which is what I'd expect, however, if I run the first test, it results in:
$obj | New-Test -Verbose
Total objects:
VERBOSE: No existing object to increment. Assuming first entry.
Total objects:
VERBOSE: No existing object to increment. Assuming first entry.
This time $objects has nothing in it.
I'm sure it's simple, but I can't fathom this one out. Any help is appreciated.
P.S. PowerShell 5.1
$obj2 is a hashtable, not an array. Hashtables are not enumerated by default, so the hashtable itself is the one object. If you want to loop through an hashtable using the pipeline you need to use $obj2.GetEnumerator().
#{"hello"="world";"foo"="bar"} | Measure-Object | Select-Object Count
Count
-----
1
#{"hello"="world";"foo"="bar"}.GetEnumerator() | Measure-Object | Select-Object Count
Count
-----
2

How can I make this MiniZinc output statement compile?

How do I revise this to compile in MiniZinc:
output [[show (P[j,p]) ++ "\n" | p in 1 .. 4] | j in 1 .. 4];
I tried several ways.
It depends on what you want to do. Here are some different approaches that will write P as a matrix. The first one write the matrix as lists ([...]), the second one just outputs the values.
output [
show([P[j,p] | p in 1 .. 4]) ++ "\n"
| j in 1 .. 4
];
output [
if p = 1 then "\n" else " " endif ++
show(P[j,p])
| j in 1 .. 4, p in 1 .. 4
];
Update: In MiniZinc 2.0 (at least in a fairly recent Git version), there is now a show2d predicate:
output [ show2d(P)];

Code Golf New Year Edition - Integer to Roman Numeral

Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
Write a program that take a single command line argument N and prints out the corresponding Roman Numeral.
Eg N = 2009 should print MMIX.
Let's say this should work for 0 < N < 3000.
(Had fun playing my first ever round of code golf with the Christmas edition, and thought this could fit for New Year. Googled to see if this has come up before elsewhere and it looks like it hasn't, but let me know if this is too hard or too easy or if the rules need changing. )
Happy MMIX!
Perl: 69 strokes (count 'em!)
Sixty-nine strokes including calling perl in the first place:
$ perl -ple's!.!($#.=5x$&*8%29628)=~y$IVCXL4620-8$XLMCDIXV$d!eg;last}{'
3484
MMMCDLXXXIV
Reads a single line, writes a single line.
Works from 0 to 3999, inclusive. (Prints empty string for 0.)
In Perl golf competitions, this is usually scored as 62 strokes = 58 for the code + 4 for the switches.
Why, yes, those are mismatched braces. Thanks for asking. =)
Credits: originally due to Ton Hospel. The trick involving the mismatched braces is from rev.pl in this post (which incidentally, is ingenious).
In C#, as an extension method to Int32:
public static class Int32Extension {
public static string ToRomanNumeral(this int number) {
Dictionary<int, string> lookup = new Dictionary<int, string>() {
{ 1000000, "M_" },
{ 900000, "C_D_" },
{ 500000, "D_" },
{ 400000, "C_D_" },
{ 100000, "C_" },
{ 90000, "X_C_" },
{ 50000, "L_" },
{ 40000, "X_L_" },
{ 10000, "X_" },
{ 9000, "MX_"},
{ 5000, "V_" },
{ 4000, "MV_" },
{ 1000, "M" },
{ 900, "CM" },
{ 500, "D" },
{ 400, "CD" },
{ 100,"C" },
{ 90, "XC" },
{ 50, "L" },
{ 40, "XL" },
{ 10, "X" },
{ 9, "IX" },
{ 5, "V" },
{ 4, "IV" },
{ 1, "I" }
};
StringBuilder answer = new StringBuilder();
foreach (int key in lookup.Keys.OrderBy(k => -1 * k)) {
while (number >= key) {
number -= key;
answer.Append(lookup[key]);
}
}
return answer.ToString();
}
}
The underscores should be overlines above the respective letter to be true Roman Numeral.
Common lisp (SBCL). 63 characters counted by "wc -c".
(format t "~#R~%" (parse-integer (elt *posix-argv* 1)))
(quit)
This only works for numbers upto 3999.
C#: 179 chars (not including spaces/tabs)
static string c(int a)
{
int[] v = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
var s = "";
for ( var i = 0; i < 13; i++ )
while (a >= v[i])
{
a -= v[i];
s += "M CM D CD C XC L XL X IX V IV I".Split()[i];
}
return s;
}
Perl, 19 strokes. Guaranteed to work for values between 1 and 12.
sub r{chr 8543+pop}
Language: JavaScript.
129 chars without the added formatting
The following code is a result of coding quiz which which took place at pl.comp.lang.javascript newsgrup several years ago. I'm not the author of the code.
function rome(N,s,b,a,o){
for(s=b='',a=5;N;b++,a^=7)for(o=N%a,N=N/a^0;o--;)
s='IVXLCDM'.charAt(o>2?b+N-(N&=~1)+(o=1):b)+s;return s
}
Original post by Elus
Python, 173 bytes.
r=lambda n:o[n]if n<10 else''.join(dict(zip('ivxlc','xlcdm'))[c]for c in r(n//10))+o[n%10]
o=' i ii iii iv v vi vii viii ix'.split(' ')
import sys
print r(int(sys.argv[1]))
(I first saw this algorithm in Gimpel's Algorithms in Snobol4; Snobol expressed it more elegantly.)
Language: C, Char count: 174
#define R(s,v)for(;n>=v;n-=v)printf(#s);
main(int n,char**a){n=atoi(a[1]);R(M,1000)R(CM,900)R(D,500)R(CD,400)R(C,100)R(XC,90)R(L,50)R(XL,40)R(X,10)R(IX,9)R(V,5)R(IV,4)R(I,1)}
Pike
60 characters, valid for 0 to 10000:
int main (int c, array a) {
write(String.int2roman((int)a[1]));
}
Perl 5.10
perl -nE'#l=qw{1 I 4 IV 5 V 9 IX 10 X 40 XL 50 L 90 XC 100 C 400 CD 500 D 900 CM 1000 M};
$o="";while(#l){$o.=pop(#l)x($_/($c=pop #l));$_%=$c;}say$o'
You input a line, it gives you the Roman numeral equivelent. This first version even lets you input more than one line.
Here is a shorter version that only works for one line, and ignores edge cases. so 4 becomes IIII instead of IV.
perl -nE'#l=qw{1 I 5 V 10 X 50 L 100 C 500 D 1000 M};
while(#l){$o.=pop(#l)x($_/($c=pop #l));$_%=$c;}say$o'
Here is what the first version would look like as a Perl script.
use 5.010;
while(<>){
#l=qw{1 I 4 IV 5 V 9 IX 10 X 40 XL 50 L 90 XC 100 C 400 CD 500 D 900 CM 1000 M};
$o="";
while(#l){
$o .= pop(#l) x ($_/($c=pop #l));
# $l = pop #l;
# $c = pop #l;
# $o .= $l x ($_/$c);
$_ %= $c;
}
say $o;
}
A simple Haskell version, that still keeps clarity. 205 characters, including white space.
l = ["M","CM","L","CD","C","XC","L","XL","X","IX","V","IV","I"]
v = [1000,900,500,400,100,90,50,40,10,9,5,4,1]
roman n i
| n == 0 = ""
| n >= v!!i = l!!i ++ roman (n-v!!i) i
| otherwise = roman n (i+1)
In Python - taken from ActiveState (credits: Paul Winkler) and compressed a bit:
def int2roman(n):
if not 0 < n < 4000: raise ValueError
ints = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
nums = ('M', 'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')
result = ""
for i in range(len(ints)):
count = int(n / ints[i])
result += nums[i] * count
n -= ints[i] * count
return result
Perl, 145 strokes (if you strip out all the newlines, which are optional), valid for 1..3999:
%t=qw(1000 M 900 CM 500 D 400 CD 100 C 90 XC 50 L 40 XL 10 X 9 IX 5 V 4 IV 1 I);
$d=pop;
for(sort{$b<=>$a}keys%t){$r.=$t{$_}x($d/$_);$d%=$_}
print$r
Some would say I could use say, but I don't have a say-capable Perl version here. Feel free to subtract 2 off the stroke count if using say works. :-)
For non-Perl programmers, this program exploits a number of useful Perl features:
Hashes are constructed from lists of even length.
Lists of strings can be specified in a compact syntax, using qw.
Strings can auto-coerce into integers, as used in the <=> comparison operator in sorting the keys.
There is an x operator which makes copies of strings/lists. Unfortunately for golfing here, x has identical precedence to /; if / were higher, the brackets would have been optional too.
Ruby, 136 chars
n = $*[0].to_i
for k,v in [1e3,900,500,400,100,90,50,40,10,9,5,4,1].zip %w{M CM D CD C XC L XL X IX V IV I}
until n < k
n -= k
print v
end
end
Language: dc (through shell) Char count:122
EDIT: q is equivalent of 2Q
dc -e '[I]1[IV]4[V]5[IX]9[X]10[XL]40[L]50[XC]90[C]100[CD]400[D]500[CM]900[M]?1000[szsz2Q]sq[~Sa[d0!<qrdPr1-lbx]dsbxLarz3<c]dscx10P' <<<$1
EDIT: two more chars by optimizing main loop stack manipulations
dc -e '[I]1[IV]4[V]5[IX]9[X]10[XL]40[L]50[XC]90[C]100[CD]400[D]500[CM]900[M]?1000[szsz2Q]sq[~Sa[d0!<qrdPr1-lbx]dsbxLarz3<c]dscx10P' <<<$1
EDIT: save 2 chars
dc -e '[I]1[IV]4[V]5[IX]9[X]10[XL]40[L]50[XC]90[C]100[CD]400[D]500[CM]900[M]1000?[sz2Q]sq[r~r[d0!<qSardPrLa1-lbx]dsbxrszz2<c]dscx10P' <<<$1
Previous version:
dc -e '[I]1[IV]4[V]5[IX]9[X]10[XL]40[L]50[XC]90[C]100[CD]400[D]500[CM]900[M]1000?[sz2Q]sq[r~r[d0!<qSaSadPLaLa1-lbx]dsbxrszz2<c]dscx10P' <<<$1
I'm no Haskell expert, and this is too long to be a winner, but here's a solution I wrote a while back to solve Euler #89.
toRoman 0 = ""
toRoman 1 = "I"
toRoman 2 = "II"
toRoman 3 = "III"
toRoman 4 = "IV"
toRoman n
| n >= 1000 = repeatRoman 'M' 1000
| n >= 900 = subtractRoman "CM" 900
| n >= 500 = subtractRoman "D" 500
| n >= 400 = subtractRoman "CD" 400
| n >= 100 = repeatRoman 'C' 100
| n >= 90 = subtractRoman "XC" 90
| n >= 50 = subtractRoman "L" 50
| n >= 40 = subtractRoman "XL" 40
| n >= 10 = repeatRoman 'X' 10
| n >= 9 = subtractRoman "IX" 9
| n >= 5 = subtractRoman "V" 5
| otherwise = error "Hunh?"
where
repeatRoman c n' = (take (n `div` n') (repeat c)) ++ (toRoman $ n `mod` n')
subtractRoman s n' = s ++ (toRoman $ n - n')
J, 20 characters!
'MDCLXVI'#~(7$5 2)#:
Usage:
'MDCLXVI'#~(7$5 2)#: 2009
MMVIIII
Okay, it doesn't do subtraction properly, but hey it's pretty cool!
Explanation;
(7$5 2)
This takes the right argument (the list 5 2) and turns it into a list of size 7 - namely 5 2 5 2 5 2 5.
(7$5 2)#: 2009
This does the "anti-base" operation - basically doing iterative div and mod operations, returning the list 2 0 0 0 0 0 1 4.
Then #~ uses the previous list as a tally to pull corresponding characters out of 'MDCLXVI'.
From a vaguely C-like language called LPC (precursor of Pike):
string roman_numeral(int val) {
check_argument(1, val, #'intp);
unless(val)
return "N";
string out = "";
if(val < 0) {
out += "-";
val = -val;
}
if(val >= 1000) {
out += "M" * (val / 1000);
val %= 1000;
}
if(val >= 100) {
int part = val / 100;
switch(part) {
case 9 :
out += "CM";
break;
case 6 .. 8 :
out += "D" + ("C" * (part - 5));
break;
case 5 :
out += "D";
break;
case 4 :
out += "CD";
break;
default :
out += "C" * part;
break;
}
val %= 100;
}
if(val >= 10) {
int part = val / 10;
switch(part) {
case 9 :
out += "XC";
break;
case 6 .. 8 :
out += "L" + ("X" * (part - 5));
break;
case 5 :
out += "L";
break;
case 4 :
out += "XL";
break;
default :
out += "X" * part;
break;
}
val %= 10;
}
switch(val) {
case 9 :
out += "IX";
break;
case 6 .. 8 :
out += "V" + ("I" * (val - 5));
break;
case 5 :
out += "V";
break;
case 4 :
out += "IV";
break;
default :
out += "I" * val;
break;
}
return out;
}
Python, 190 bytes. Based on snippet from ActiveState, via Federico.
A few small optimisations: removal of superfluous int() call, splitting string to get array, remove whitespace, ...
import sys
n=int(sys.argv[1])
N=(1000,900,500,400,100,90,50,40,10,9,5,4,1)
r=""
for i in range(len(N)):
c=n/N[i]
r+='M,CM,D,CD,C,XC,L,XL,X,IX,V,IV,I'.split(',')[i]*c
n-=N[i]*c
print r
EDIT: superfluous, not spurious, and remove range check - thanks to Chris and dreeves! Stole idea of using symbol array inline from balabaster.
VB: 193 chars
Function c(ByVal a)
Dim v() = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
Dim s = ""
For i = 0 To 12
While a >= v(i)
a -= v(i)
s += "M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I".Split("|")(i)
End While
Next
Return s
End Function
Excel 8 chars (not lincluding the number):
=ROMAN(N)
Works up to 3000.
Tongue in cheek
Real simple: pass the query to Google and screenscrape the answer. Next. :p
BTW, shouldn't this be a community wiki?
Java: 286 significant characters
public class R {
String[]x="M,CM,D,C,XC,L,X,IX,V,I".split(",");
int[]n={1000,900,500,100,90,50,10,9,5,1};
String p(String s,int d,int i){return 10<=i?s:n[i]<=d?p(s+x[i],d-n[i],i):p(s,d,i+1);}
public static void main(String[] a) {
System.out.println(new R().p("",Integer.parseInt(a[0]),0));
}
}
By "significant characters", I mean the printing characters and required spaces (e.g. between type and argument), but not pure cosmetic whitespace (newlines and indentation).
Delphi (or Pascal, there's nothing Delphi-specific here):
Function ToRoman(N : Integer) : String;
Const
V : Array [1..13] of Word = (1000,900,500,400,100,90,50,40,10.9,5,4,1);
T : Array [1..13] of String = ('M','CM','D','CD','C','XC','L','XL','X','IX','V','I');
Var I : Word;
Begin
I := 1;
Repeat
While N < V[I] do Inc(I);
Result := Result + T[I];
N := N - V[I];
Until N = 0;
End;
How is everyone getting the character counts? (I count 8 essential spaces, all the rest are simply for formatting.)
Here is a C solution in 252 meaningful chars. Valid from 0 <= i < 4000. Mostly I wrote this because so many solutions include IV and IX at array points. Decoding it: t is our temp buffer that we back fill so that we don't have to reverse it on output. The buffer passed in must be at least 16 chars (for 3888 -> MMMDCCCLXXXVIII).
char* i2r(int i, char* r) {
char t[20];
char* o=t+19;*o=0;
char* s="IVXLCDMM";
for (char*p=s+1;*p&&i;p+=2) {
int x=i%10;
if (x==9) {*--o=p[1];*--o=p[-1];}
else if (x==4) {*--o=*p;*--o=p[-1];}
else {
for(;x&&x!=5;--x)*--o=p[-1];
if(x)*--o=*p;
}
i/=10;
}
return strcpy(r,o);
}
And I always forget to put the main on. So much for 252 chars:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main(int a,char**v){
char buf[16];
printf("%s\n",i2r(atoi(v[1])));
}
Language: C, Char count: 195
Based heavily off of me.yahoo.com/joe_mucchielle's C solution:
char t[99],*o=t+99,*s="IVXLCDMM",*p,x;n(v){*--o=p[v];}main(int i,int**v){i=atoi(v[1]);*o=0;
for(p=s+1;*p&&i;p+=2){x=i%10;if(x%5==4)n(x==9),n(-1);else{for(;x%5;--x)n(-1);if(x)n(0);}i/=10;}puts(o);}
Language: Erlang, Char count: 222
EDIT2: Erlang preprocessor allows some sort of unbalanced macros so this version is 9 chars shorter.
-module(n2).
-export([y/1]).
-define(D(V,S),n(N)when N>=V->[??S|n(N-V)];).
y(N)->io:format(n(N)).
?D(1000,M)?D(900,CM)?D(500,D)?D(400,CD)?D(100,C)?D(90,XC)?D(50,L)?D(40,XL)?D(10,X)?D(9,IX)?D(5,V)?D(4,IV)?D(1,I)n(0)->[10].
EDIT: Shorter version inspired by Darius version (231 chars)
-module(n).
-export([y/1]).
y(N)->io:format([n(N),10]).
n(N)when N>9->[Y||C<-n(N div 10),{K,Y}<-lists:zip("IVXLC","XLCDM"),K==C]++o(N rem 10);n(N)->o(N).
o(N)->lists:nth(N+1,[[]|string:tokens("I II III IV V VI VII VIII IX"," ")]).
It's less readable but save 2 chars (233 chars).
-module(n).
-export([y/1]).
-define(D(V,S),n(N)when N>=V->[??S|n(N-V)]).
y(N)->io:format(n(N)).
?D(1000,M);?D(900,CM);?D(500,D);?D(400,CD);?D(100,C);?D(90,XC);?D(50,L);?D(40,XL);?D(10,X);?D(9,IX);?D(5,V);?D(4,IV);?D(1,I);n(0)->[10].
Command line version:
-module(n).
-export([y/1]).
-define(D(V,S),n(N)when N>=V->[??S|n(N-V)]).
y([N])->io:format(n(list_to_integer(N))),init:stop().
?D(1000,M);?D(900,CM);?D(500,D);?D(400,CD);?D(100,C);?D(90,XC);?D(50,L);?D(40,XL);?D(10,X);?D(9,IX);?D(5,V);?D(4,IV);?D(1,I);n(0)->[10].
Invocation:
$ erl -noshell -noinput -run n y 2009
MMIX
EDIT: I saved 17 chars using literal macro expansion.
Railo CFML - 53 chars, 46 without whitespace...
<cfoutput>
#NumberFormat( N , 'roman' )#
</cfoutput>
Or, for other CF engines, not sure if these are shortest, but they'll do for now...
CFML - 350..453 characters:
<cffunction name="RomanNumberFormat">
<cfset var D = ListToArray('M,CM,D,C,XC,L,X,IX,V,IV,I') />
<cfset var I = [1000,900,500,100,90,50,10,9,5,4,1] />
<cfset var R = '' />
<cfset var x = 1 />
<cfset var A = Arguments[1] />
<cfloop condition="A GT 0">
<cfloop condition="A GTE I[x]">
<cfset R &= D[x] />
<cfset A -= I[x] />
</cfloop>
<cfset x++ />
</cfloop>
<cfreturn R />
</cffunction>
<cfoutput>
#RomanNumberFormat(N)#
</cfoutput>
CFScript - 219..323 characters:
<cfscript>
function RomanNumberFormat(A)
{
var D = ListToArray('M,CM,D,C,XC,L,X,IX,V,IV,I');
var I = [1000,900,500,100,90,50,10,9,5,4,1];
var R = '';
var x = 1;
while ( A > 0 )
{
while( A >= I[x] )
{
R &= D[x];
A -= I[x];
}
x++;
}
return R;
}
WriteOutput( RomanNumberFormat(N) );
</cfscript>
In C# (running on .NET 4 RC), with 335 chars (if you remove the extraneous formatting).
using System;
using System.Linq;
class C
{
static void Main()
{
Console.WriteLine(
Console.ReadLine()
.PadLeft(4,'0')
.Select(d=>d-'0')
.Zip(new[]{" M","MDC","CLX","XVI"},(x,y)=>new{x,y})
.Aggregate("",(r,t)=>r+
new string(t.y[2],t.x%5/4)+
new string(t.y[0],t.x%5/4*t.x/5)+
new string(t.y[1],Math.Abs(t.x%5/4-t.x/5))+
new string(t.y[2],t.x%5%4)));
}
}
I know it does not beat the current best C# answer (182 chars) but this is just one big LINQ one-liner. Once I saw a raytracer written as a single LINQ query, I started approaching code golfs from this perspective.
Since this approach is functional, I'm working on a Haskell version of the same algorithm (will surely be shorter).
Haskell version of my C#/LINQ answer, 234 chars:
main=putStrLn.foldr(\(y,x)s->concat[s,r(a x)(y!!2),r(a x*div x 5)(y!!0),r(abs(a x-div x 5))(y!!1),r(mod(mod x 5)4)(y!!2)])"".zip["XVI","CLX","MDC"," M"].map(read.(:[])).take 4.(++"000").reverse=<<getLine
r=replicate
a x=div(mod x 5)4