Skip to content

Instantly share code, notes, and snippets.

@yamadapc
Last active January 3, 2016 07:19
Show Gist options
  • Save yamadapc/8428269 to your computer and use it in GitHub Desktop.
Save yamadapc/8428269 to your computer and use it in GitHub Desktop.
roman2decimal
'use strict';
module.exports = function(roman) {
var roman_array = roman.split('');
var dec_array = roman_array.map(function(roman_alg) {
return ROMAN_MAP[roman_alg];
});
var same = 1;
return dec_array.reduce(function(memo, n, index, list) {
var next = list[index + 1], prev = list[index - 1];
var n_prec = PRECEDENCE_MAP.indexOf(n), next_prec = PRECEDENCE_MAP.indexOf(next);
// Validate repetition
if(n === next) {
same += 1;
if(same > 3) throw new Error('Only 3 equal algorisms may be consecutive');
}
else same = 1;
// Validate 'precedence'
if(next_prec > n_prec + 2 || (n === prev && next_prec > n_prec))
throw new Error('Input must obey to the precendence map');
// Subtractive case
if(next && n < next) {
// Validate subtractive redundancy
if(next - n === n)
throw new Error('Input may not be redundant');
return memo - n;
}
// Aditive case
else {
if(next + n === PRECEDENCE_MAP[PRECEDENCE_MAP.indexOf(n) + 1])
throw new Error('Input may not be redundant');
return memo + n;
}
}, 0);
};
/**
* Constants and mappings
* --------------------------------------------------------------------------*/
var ROMAN_MAP = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000
};
var PRECEDENCE_MAP = [1, 5, 10, 50, 100, 500, 1000];
ROMAN_MAP = {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
result = 0
roman = [ROMAN_MAP[c] for c in raw_input()]
for i, n in enumerate(roman):
nxt = 0 if len(roman) == i + 1 else roman[i + 1]
result = result - n if nxt > n else result + n
print result
ROMAN_MAP = {'I'=>1,'V'=>5,'X'=>10,'L'=>50,'C'=>100,'D'=>500,'M'=>1000}
roman = gets.chomp.split ''
result = 0
roman.each_with_index do |roman_alg, i|
nxt = ROMAN_MAP[roman[i + 1]]
n = ROMAN_MAP[roman_alg]
result = (nxt and nxt > n) ? result - n : result + n
end
puts result
'use strict'; /* global describe, it */
/**
* Dependencies
* --------------------------------------------------------------------------*/
require('should');
var roman_to_dec = require('..');
describe('roman2decimal', function() {
it('works purely additive input', function() {
roman_to_dec('CCC').should.equal(300);
roman_to_dec('CCCXX').should.equal(320);
});
it('works with subtractive input', function() {
roman_to_dec('XC').should.equal(90);
roman_to_dec('IV').should.equal(4);
roman_to_dec('CD').should.equal(400);
roman_to_dec('IX').should.equal(9);
});
it('should work on mixed input', function() {
roman_to_dec('MMMCCXC').should.equal(3290);
roman_to_dec('MCMVII').should.equal(1907);
roman_to_dec('MMCDXLVII').should.equal(2447);
roman_to_dec('MMCMLXXXIV').should.equal(2984);
roman_to_dec('MXCVI').should.equal(1096);
roman_to_dec('MCMIV').should.equal(1904);
roman_to_dec('MMDII').should.equal(2502);
roman_to_dec('M').should.equal(1000);
roman_to_dec('MMDLXXIX').should.equal(2579);
roman_to_dec('MMMLXXXVIII').should.equal(3088);
roman_to_dec('MMDCCXCIX').should.equal(2799);
roman_to_dec('CXCVII').should.equal(197);
});
it('throws errors on invalid input', function() {
// Precedence errors
roman_to_dec.bind(null, 'CCM').should.throw();
roman_to_dec.bind(null, 'IC').should.throw();
roman_to_dec.bind(null, 'IVX').should.throw();
roman_to_dec.bind(null, 'MMID').should.throw();
roman_to_dec.bind(null, 'IIIC').should.throw();
roman_to_dec.bind(null, 'IVX').should.throw();
// Over-consecutiveness errors
roman_to_dec.bind(null, 'XIIII').should.throw();
roman_to_dec.bind(null, 'XXXX').should.throw();
// Redundancy errors
roman_to_dec.bind(null, 'XXXVVI').should.throw();
roman_to_dec.bind(null, 'LC').should.throw();
roman_to_dec.bind(null, 'VX').should.throw();
roman_to_dec.bind(null, 'VXX').should.throw();
});
});
module.exports = function(roman) {
return roman.split('').reduce(function(memo, roman_alg, index, list) {
var n = ROMAN_MAP[roman_alg], next = ROMAN_MAP[list[index + 1]];
return (next && next > n) ? memo - n : memo + n;
}, 0);
};
var ROMAN_MAP = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000 };
@yamadapc
Copy link
Author

Revision 2 fixes spelling and formatting.

@yamadapc
Copy link
Author

Revision 3 adds my test suite

@yamadapc
Copy link
Author

Revision 4 cleans up the test suite and adds some comments to it
though I'm probably wrong, I can't see more cases than these

@yamadapc
Copy link
Author

Revision 5 cleans-up the code

@yamadapc
Copy link
Author

The fact I'm using a same captured variable inside the reduction function is really shitty and breaks the point of using a reduce in the first place.

@yamadapc
Copy link
Author

The fact that I have to use a prev variable means my algorithm performs one of the validations twice for each element - which is also pretty shitty.

@yamadapc
Copy link
Author

Revisions 6 and 7 add a version without any validations.
(this is only to demonstrate how trivial the initial problem seems to be; these 8 lines pass the whole suite but the invalid input cases)

@yamadapc
Copy link
Author

Revision 8 adds a tiny python solution without validation

@yamadapc
Copy link
Author

Revision 9 adds another tiny ingenious implementation in ruby without validation

@yamadapc
Copy link
Author

Revisions 10 and 11 use ternary operators (though not quite in python) to remove a line from the "mini" javascript version and a couple from the python version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment