patternjavascriptMinor
Javascript velocity converter, fully chainable
Viewed 0 times
javascriptvelocityfullyconverterchainable
Problem
I created this out of curiosity and it has some code duplication issues and the fact that sticking
How would you improve it?
```
/**
* A very special velocity converter
* Designed mainly for a very natural syntax
* It allows full chainable conversion calls and maintains its on internal state.
*
* example:
* remotewind.convert(10).knots().to.mph().to.kmh().yield();
*
* Each unit has a yield "method" which returns the last value (the internal state)
*
* @param val
* @return Converter
*/
remotewind.convert = function(val) {
var val = val;
function Unit(){
this.yield = function(){
return val;
}
}
function Ms(ms){
val = ms;
this.to = {
kmh : function(){ return new Kmh(val * 3.6) },
mph : function(){ return new Mph(val * 2.2369362920544) },
knots: function(){ return new Knots(val * 1.943844492) }
}
}
Ms.prototype = new Unit();
function Kmh(kmh){
val = kmh;
this.to = {
ms : function(){ return new Ms(val * 0.277777778) },
mph : function(){ return new Mph(val * 0.621371192) },
knots: function(){ return new Knots(val * 0.539956803) }
}
}
Kmh.prototype = new Unit();
function Mph(mph){
val = mph;
this.to = {
ms : function(){ return new Ms(val * 0.44704) },
kmh : function(){ return new Kmh(val * 0.621371192) },
knots: function(){ return new Knots(val * 0.868976242) }
}
}
Mph.prototype = new Unit();
function Knots(kn){
val = kn;
this.to = {
ms : function(){ return new Ms(val * 0.514444444) },
mph : function(){ return new Mph(val * 1.150779448) },
knots: function(){ return new Knots(val * 1.852) }
}
}
Knots.prototype = new Unit();
function Converter(val){
.yield() on the end of a call to a unit converter is pretty strange.How would you improve it?
```
/**
* A very special velocity converter
* Designed mainly for a very natural syntax
* It allows full chainable conversion calls and maintains its on internal state.
*
* example:
* remotewind.convert(10).knots().to.mph().to.kmh().yield();
*
* Each unit has a yield "method" which returns the last value (the internal state)
*
* @param val
* @return Converter
*/
remotewind.convert = function(val) {
var val = val;
function Unit(){
this.yield = function(){
return val;
}
}
function Ms(ms){
val = ms;
this.to = {
kmh : function(){ return new Kmh(val * 3.6) },
mph : function(){ return new Mph(val * 2.2369362920544) },
knots: function(){ return new Knots(val * 1.943844492) }
}
}
Ms.prototype = new Unit();
function Kmh(kmh){
val = kmh;
this.to = {
ms : function(){ return new Ms(val * 0.277777778) },
mph : function(){ return new Mph(val * 0.621371192) },
knots: function(){ return new Knots(val * 0.539956803) }
}
}
Kmh.prototype = new Unit();
function Mph(mph){
val = mph;
this.to = {
ms : function(){ return new Ms(val * 0.44704) },
kmh : function(){ return new Kmh(val * 0.621371192) },
knots: function(){ return new Knots(val * 0.868976242) }
}
}
Mph.prototype = new Unit();
function Knots(kn){
val = kn;
this.to = {
ms : function(){ return new Ms(val * 0.514444444) },
mph : function(){ return new Mph(val * 1.150779448) },
knots: function(){ return new Knots(val * 1.852) }
}
}
Knots.prototype = new Unit();
function Converter(val){
Solution
Here's a fairly cryptic rewrite, focussing mostly on extensibility and minimal repetition. The trick I'm using is to pick one unit as our "base" unit from which all others are derived. The base-unit value (m/s in this case) is carried through the chain.
Edit: Just for fun, here's an ultra-convoluted version that handles different measurement types. Couldn't get temperature conversions in there, as simple multiplication/division isn't enough to deal with C/K/F conversions. Boo.
Really though, the code's pretty dense, and I can't recommend coding like this. But again, this was just for fun.
Anyway, the usage would be something like
And here's the hairy code:
var convert = (function () {
var conversions = {
ms: 1, // use m/s as our base unit
kmh: 3.6,
mph: 2.23693629,
knots: 1.94384449
// feel free to add more
};
function Unit(unit, ms) {
this.value = ms * conversions[unit];
this.to = {};
for(var otherUnit in conversions) {
(function (target) {
this.to[target] = function () {
return new Unit(target, ms);
}
}).call(this, otherUnit);
}
}
Unit.prototype = {
yield: function () {
return this.valueOf();
},
toString: function () {
return String(this.value);
},
valueOf: function () {
return this.value;
}
};
return function (value) {
var units = {};
for(var unit in conversions) {
(function (unit) {
units[unit] = function () {
return new Unit(unit, value / conversions[unit]);
};
}(unit));
}
return units;
}
}());Edit: Just for fun, here's an ultra-convoluted version that handles different measurement types. Couldn't get temperature conversions in there, as simple multiplication/division isn't enough to deal with C/K/F conversions. Boo.
Really though, the code's pretty dense, and I can't recommend coding like this. But again, this was just for fun.
Anyway, the usage would be something like
convert.speed(60).mph().to.kmh(); // ~97km/h
convert.distance(100).m().to.ft(); // ~330ft
convert.mass(1000).g().to.lb(); // ~2.2lbAnd here's the hairy code:
var convert = (function () {
var conversions = {
speed: {
ms: 1, // use m/s as our base unit
kmh: 3.6,
mph: 2.23693629,
knots: 1.94384449
},
distance: {
m: 1, // use meters as our base
inches: 39.3700787402, // can't use "in" as that's a keyword. Darn.
ft: 3.280839895,
mi: 0.000621371192,
nm: 0.000539956803 // nautical miles, not nanometers
},
mass: {
g: 1, // use grams as our base
lb: 0.002204622622,
oz: 0.0352739619
}
};
function Unit(type, unit, base) {
this.value = base * conversions[type][unit];
this.to = {};
for(var otherUnit in conversions[type]) {
(function (target) {
this.to[target] = function () {
return new Unit(type, target, base);
}
}).call(this, otherUnit);
}
}
Unit.prototype = {
yield: function () {
return this.valueOf();
},
toString: function () {
return String(this.value);
},
valueOf: function () {
return this.value;
}
};
// my god, it's full of scopes!
var types = {};
for(var type in conversions) {
(function (type) {
types[type] = function (value) {
var units = {};
for(var unit in conversions[type]) {
(function (unit) {
units[unit] = function () {
return new Unit(type, unit, value / conversions[type][unit]);
}
}(unit));
}
return units;
};
}(type));
}
return types;
}());Code Snippets
var convert = (function () {
var conversions = {
ms: 1, // use m/s as our base unit
kmh: 3.6,
mph: 2.23693629,
knots: 1.94384449
// feel free to add more
};
function Unit(unit, ms) {
this.value = ms * conversions[unit];
this.to = {};
for(var otherUnit in conversions) {
(function (target) {
this.to[target] = function () {
return new Unit(target, ms);
}
}).call(this, otherUnit);
}
}
Unit.prototype = {
yield: function () {
return this.valueOf();
},
toString: function () {
return String(this.value);
},
valueOf: function () {
return this.value;
}
};
return function (value) {
var units = {};
for(var unit in conversions) {
(function (unit) {
units[unit] = function () {
return new Unit(unit, value / conversions[unit]);
};
}(unit));
}
return units;
}
}());convert.speed(60).mph().to.kmh(); // ~97km/h
convert.distance(100).m().to.ft(); // ~330ft
convert.mass(1000).g().to.lb(); // ~2.2lbvar convert = (function () {
var conversions = {
speed: {
ms: 1, // use m/s as our base unit
kmh: 3.6,
mph: 2.23693629,
knots: 1.94384449
},
distance: {
m: 1, // use meters as our base
inches: 39.3700787402, // can't use "in" as that's a keyword. Darn.
ft: 3.280839895,
mi: 0.000621371192,
nm: 0.000539956803 // nautical miles, not nanometers
},
mass: {
g: 1, // use grams as our base
lb: 0.002204622622,
oz: 0.0352739619
}
};
function Unit(type, unit, base) {
this.value = base * conversions[type][unit];
this.to = {};
for(var otherUnit in conversions[type]) {
(function (target) {
this.to[target] = function () {
return new Unit(type, target, base);
}
}).call(this, otherUnit);
}
}
Unit.prototype = {
yield: function () {
return this.valueOf();
},
toString: function () {
return String(this.value);
},
valueOf: function () {
return this.value;
}
};
// my god, it's full of scopes!
var types = {};
for(var type in conversions) {
(function (type) {
types[type] = function (value) {
var units = {};
for(var unit in conversions[type]) {
(function (unit) {
units[unit] = function () {
return new Unit(type, unit, value / conversions[type][unit]);
}
}(unit));
}
return units;
};
}(type));
}
return types;
}());Context
StackExchange Code Review Q#35955, answer score: 4
Revisions (0)
No revisions yet.