Is there a simple way (hoping for a small function, not a library) to add or subtract from a 64bit integer in JavaScript?
Example 64bit int: 291270990346989568
Background: I am working with the Twitter API which has 64bit tweet ID's. I'd like to add or subtract one from those IDs to manipulate what results I get back from Twitter.
The following code does what you've described.
Splitting, adding or subtracting, then re-joining...
This code has both increment and decrement functions, and a test function with some edge cases. When splitting number strings and doing math on them, you need to consider what happens to "leading zeroes", so there's a padding function for that.
Thanks, Justin, for providing a JSFiddle with this solution.
/*
* Prepend zeros to expand a given string to given length
*
* #var {String} numStr Number
* #var {Number} len Length to pad out to
*
* #returns {String}
*/
function pad0 (numStr,len) {
while (numStr.length < len) {
numStr = "0" + numStr;
}
return numStr
}
/*
* Decrement the given (64 bit) integer.
*
* #var {String} int64 Postive non-zero integer, as a string
*
* #returns {String}
*/
function decrInt64 (int64) {
var result = "";
var midpt = Math.floor(int64.length/2);
var upper = int64.substring(0,midpt);
var lower = int64.substring(midpt);
var upperVal = new Number(upper);
var lowerVal = new Number(lower);
if (lowerVal == 0) {
if (upperVal == 0) {
// We don't support negative numbers
result = "*ERROR*"
}
else {
// borrow 1
result = pad0((--upperVal).toString(),upper.length) +
(new Number("1"+lower) - 1).toString();
}
}
else {
var newLower = (lowerVal - 1).toString();
result = upper + pad0(newLower,lower.length);
}
alert(result);
}
/*
* Increment the given (64 bit) integer.
*
* #var {String} int64 Postive, as a string
*
* #returns {String}
*/
function incrInt64 (int64) {
var result = "";
var midpt = Math.floor(int64.length/2);
var upper = int64.substring(0,midpt);
var lower = int64.substring(midpt);
var upperVal = new Number(upper);
var lowerVal = new Number(lower);
var newLower = (++lowerVal).toString();
// Did we overflow?
if (lower.length < newLower.length) {
// Yes, carry the 1
result = (++upperVal).toString() + newLower.substring(1);
}
else {
result = upper + pad0(newLower,lower.length);
}
alert(result);
}
// Test function
window.displaymessage= function ()
{
decrInt64("291270990046989568");
incrInt64("291270990046989568");
decrInt64("000000000000000000");
incrInt64("000000000000000000");
decrInt64("000000001000000000");
incrInt64("000000001000000000");
decrInt64("099999999999999999");
incrInt64("099999999999999999");
decrInt64("999999999999999999");
incrInt64("999999999999999999");
}
Addition of integer-formatted strings (base 10) of indefinite lengths can be done by splitting the string into segments of 9 characters, calculating + or - for that and then moving to the preceding 9 characters. This is because the largest 9 character number, 999999999 is 32-bit safe (as is 1999999999, which I used in subtraction).
The following code has 3 functions and the word integer is assumed to mean an integer-formatted string.
addAsString, takes two non-negative integers x and y, returns x + y
subtractAsString, takes two non-negative integers x and y, |x| >= |y|, returns x - y
addORsub, takes any two integers x and y, returning x + y
I've tried to explain what is happening via comments in the code
// Indefinate length addition
function addAsString(x, y) { // x, y strings
var s = '';
if (y.length > x.length) { // always have x longer
s = x;
x = y;
y = s;
}
s = (parseInt(x.slice(-9),10) + parseInt(y.slice(-9),10)).toString(); // add last 9 digits
x = x.slice(0,-9); // cut off last 9 digits
y = y.slice(0,-9);
if (s.length > 9) { // if >= 10, add in the 1
if (x === '') return s; // special case (e.g. 9+9=18)
x = addAsString(x, '1');
s = s.slice(1);
} else if (x.length) { // if more recursions to go
while (s.length < 9) { // make sure to pad with 0s
s = '0' + s;
}
}
if (y === '') return x + s; // if no more chars then done, return
return addAsString(x, y) + s; // else recurse, next digit
}
// Indefinate length subtraction (x - y, |x| >= |y|)
function subtractAsString(x, y) {
var s;
s = (parseInt('1'+x.slice(-9),10) - parseInt(y.slice(-9),10)).toString(); // subtract last 9 digits
x = x.slice(0,-9); // cut off last 9 digits
y = y.slice(0,-9);
if (s.length === 10 || x === '') { // didn't need to go mod 1000000000
s = s.slice(1);
} else { // went mod 1000000000, inc y
if (y.length) { // only add if makes sense
y = addAsString(y, '1');
} else { // else set
y = '1';
}
if (x.length) {
while (s.length < 9) { // pad s
s = '0' + s;
}
}
}
if (y === '') { // finished
s = (x + s).replace(/^0+/,''); // dont return all 0s
return s;
}
return subtractAsString(x, y) + s;
}
// Indefinate length addition or subtraction (via above)
function addORsub(x, y) {
var s = '';
x = x.replace(/^(-)?0+/,'$1').replace(/^-?$/,'0'); // -000001 = -1
y = y.replace(/^(-)?0+/,'$1').replace(/^-?$/,'0'); // -000000 = 0
if (x[0] === '-') { // x negative
if (y[0] === '-') { // if y negative too
return '-' + addAsString(x.slice(1), y.slice(1)); // return -(|x|+|y|)
}
return addORsub(y, x); // else swap
}
if (y[0] === '-') { // x positive, y negative
s = y.slice(1);
if (s.length < x.length || (s.length === x.length && s < x)) return subtractAsString(x, s) || '0'; // if |x|>|y|, return x-y
if (s === x) return '0'; // equal then 0
s = subtractAsString(s, x); // else |x|<|y|
s = (s && '-' + s) || '0';
return s; // return -(|y|-x)
}
return addAsString(x, y); // x, y positive, return x+y
}
Example usage (fiddle)
var i = addORsub('291270990346989568', '1'); // add
i === '291270990346989569';
i = addORsub('291270990346989568', '-1'); // subtract
i === '291270990346989567';
Related
I have a signed value given as a hex number, by example 0xffeb and want convert it into -21 as a "normal" Javascript integer.
I have written some code so far:
function toBinary(a) { //: String
var r = '';
var binCounter = 0;
while (a > 0) {
r = a%2 + r;
a = Math.floor(a/2);
}
return r;
}
function twoscompl(a) { //: int
var l = toBinaryFill(a).length;
var msb = a >>> (l-1);
if (msb == 0) {
return a;
}
a = a-1;
var str = toBinary(a);
var nstr = '';
for (var i = 0; i < str.length; i++) {
nstr += str.charAt(i) == '1' ? '0' : '1';
}
return (-1)*parseInt(nstr);
}
The problem is, that my function returns 1 as MSB for both numbers because only at the MSB of the binary representation "string" is looked. And for this case both numbers are 1:
-21 => 0xffeb => 1111 1111 1110 1011
21 => 0x15 => 1 0101
Have you any idea to implement this more efficient and nicer?
Greetings,
mythbu
Use parseInt() to convert (which just accepts your hex string):
parseInt(a);
Then use a mask to figure out if the MSB is set:
a & 0x8000
If that returns a nonzero value, you know it is negative.
To wrap it all up:
a = "0xffeb";
a = parseInt(a, 16);
if ((a & 0x8000) > 0) {
a = a - 0x10000;
}
Note that this only works for 16-bit integers (short in C). If you have a 32-bit integer, you'll need a different mask and subtraction.
I came up with this
function hexToInt(hex) {
if (hex.length % 2 != 0) {
hex = "0" + hex;
}
var num = parseInt(hex, 16);
var maxVal = Math.pow(2, hex.length / 2 * 8);
if (num > maxVal / 2 - 1) {
num = num - maxVal
}
return num;
}
And usage:
var res = hexToInt("FF"); // -1
res = hexToInt("A"); // same as "0A", 10
res = hexToInt("FFF"); // same as "0FFF", 4095
res = hexToInt("FFFF"); // -1
So basically the hex conversion range depends on hex's length, ant this is what I was looking for. Hope it helps.
Based on #Bart Friederichs I've come with:
function HexToSignedInt(num, numSize) {
var val = {
mask: 0x8 * Math.pow(16, numSize-1), // 0x8000 if numSize = 4
sub: -0x1 * Math.pow(16, numSize) //-0x10000 if numSize = 4
}
if((parseInt(num, 16) & val.mask) > 0) { //negative
return (val.sub + parseInt(num, 16))
}else { //positive
return (parseInt(num,16))
}
}
so now you can specify the exact length (in nibbles).
var numberToConvert = "CB8";
HexToSignedInt(numberToConvert, 3);
//expected output: -840
function hexToSignedInt(hex) {
if (hex.length % 2 != 0) {
hex = "0" + hex;
}
var num = parseInt(hex, 16);
var maxVal = Math.pow(2, hex.length / 2 * 8);
if (num > maxVal / 2 - 1) {
num = num - maxVal
}
return num;
}
function hexToUnsignedInt(hex){
return parseInt(hex,16);
}
the first for signed integer and
the second for unsigned integer
As I had to turn absolute numeric values to int32 values that range from -2^24 to 2^24-1,
I came up with this solution, you just have to change your input into a number through parseInt(hex, 16), in your case, nBytes is 2.
function toSignedInt(value, nBytes) { // 0 <= value < 2^nbytes*4, nBytes >= 1,
var hexMask = '0x80' + '00'.repeat(nBytes - 1);
var intMask = parseInt(hexMask, 16);
if (value >= intMask) {
value = value - intMask * 2;
}
return value;
}
var vals = [ // expected output
'0x00', // 0
'0xFF', // 255
'0xFFFFFF', // 2^24 - 1 = 16777215
'0x7FFFFFFF', // 2^31 -1 = 2147483647
'0x80000000', // -2^31 = -2147483648
'0x80000001', // -2^31 + 1 = -2147483647
'0xFFFFFFFF', // -1
];
for (var hex of vals) {
var num = parseInt(hex, 16);
var result = toSignedInt(num, 4);
console.log(hex, num, result);
}
var sampleInput = '0xffeb';
var sampleResult = toSignedInt(parseInt(sampleInput, 16), 2);
console.log(sampleInput, sampleResult); // "0xffeb", -21
Based on the accepted answer, expand to longer number types:
function parseSignedShort(str) {
const i = parseInt(str, 16);
return i >= 0x8000 ? i - 0x10000 : i;
}
parseSignedShort("0xffeb"); // -21
function parseSignedInt(str) {
const i = parseInt(str, 16);
return i >= 0x80000000 ? i - 0x100000000 : i;
}
parseSignedInt("0xffffffeb"); // -21
// Depends on new JS feature. Only supported after ES2020
function parseSignedLong(str) {
if (!str.toLowerCase().startsWith("0x"))
str = "0x" + str;
const i = BigInt(str);
return Number(i >= 0x8000000000000000n ? i - 0x10000000000000000n : i);
}
parseSignedLong("0xffffffffffffffeb"); // -21
I'm using Angular "currency" filter to show price in a shopping cart. The prices are fetched from a back end server. So sometimes the price may not be available to show to the user. In that case I just want to show the user that the price is not available in the same field as of currency field. I can not show plain text in a currency filter. The only solution I found is to keep another text field to show/hide when a price is not available.But this is some what unnecessary I think. Is there any way to extend or override the built in "currency" filter of Angular js ?. Kindly appreciate some help.
<div class="large-12 medium-12 small-12 columns pad-none nzs-pro-list-item-price">
{{item.ItmPrice|currency}}
</div>
Create your custom filter which will internally use currency when value is present, otherwise it will return text which you want to show instead.
Markup
{{amount | customFormat:"USD$": "N/A"}}
Filter
.filter('customFormat', function($filter) {
return function(value, format, text) {
if (angular.isDefined(value) && value != null)
return $filter('currency')(value, format);
else
return text;
}
});
Working Plunkr
I feel the best way is to rewrite currency filter, so that you have fill control over Pattern, Grouping Separator, Decimal Separator & symbol position
<span>
{{26666662.5226 | fmtCurrency :"##.###,###" : "." : "," : "$" : "first"}}
</span>
Resluts in: $26.666.662,523
Filter:
app.filter("fmtCurrency", ['CurrencyService', function sasCurrency(CurrencyService) {
return function (amount, pattern, groupingSeparator, decimalSeparator, currencySymbol, symbolPosition) {
var patternInfo = CurrencyService.parsePattern(pattern, groupingSeparator, decimalSeparator);
var formattedCurrency = CurrencyService.formatCurrency(amount, patternInfo, groupingSeparator, decimalSeparator);
if (symbolPosition === 'last')
return formattedCurrency + currencySymbol;
else
return currencySymbol + formattedCurrency;
};
}])
Service: formatNumber which is the same function used in angular currency filter is being used here inside service
app.service("CurrencyService", function () {
var PATTERN_SEP = ';',
DECIMAL_SEP = '.',
GROUP_SEP = ',',
ZERO = '0',
DIGIT = "#";
var MAX_DIGITS = 22;
var ZERO_CHAR = '0';
return {
parsePattern: function (pattern, groupingSeparator, decimalSeparator) {
return parsePattern(pattern, groupingSeparator, decimalSeparator);
},
formatCurrency: function (amount, patternInfo, groupingSeparator, decimalSeparator) {
return formatNumber(amount, patternInfo, groupingSeparator, decimalSeparator);
}
}
/*
* Currency formatter utility
*/
function isUndefined(value) { return typeof value === 'undefined'; }
/**
* main function for parser
* #param str {string} pattern to be parsed (e.g. #,##0.###).
*/
function parsePattern(pattern, groupSep, decimalSep) {
DECIMAL_SEP = decimalSep;
GROUP_SEP = groupSep;
var p = {
minInt: 1,
minFrac: 0,
maxFrac: 0,
posPre: '',
posSuf: '',
negPre: '',
negSuf: '',
gSize: 0,
lgSize: 0
};
var ZERO = '0',
DIGIT = "#";
var parts = pattern.split(PATTERN_SEP),
positive = parts[0],
negative = parts[1];
var parts = positive.split(DECIMAL_SEP),
integer = parts[0],
fraction = parts[1];
console.log(parts);
p.posPre = integer.substr(0, integer.indexOf(DIGIT));
if (fraction) {
for (var i = 0; i < fraction.length; i++) {
var ch = fraction.charAt(i);
console.log(ch, ZERO, DIGIT);
if (ch == ZERO)
p.minFrac = p.maxFrac = i + 1;
else if (ch == DIGIT)
p.maxFrac = i + 1;
else
p.posSuf += ch;
}
}
var groups = integer.split(GROUP_SEP);
p.gSize = groups[1] ? groups[1].length : 0;
p.lgSize = (groups[2] || groups[1]) ? (groups[2] || groups[1]).length : 0;
if (negative) {
var trunkLen = positive.length - p.posPre.length - p.posSuf.length,
pos = negative.indexOf(DIGIT);
p.negPre = negative.substr(0, pos).replace(/\'/g, '');
p.negSuf = negative.substr(pos + trunkLen).replace(/\'/g, '');
} else {
// hardcoded '-' sign is fine as all locale use '-' as MINUS_SIGN. (\u2212 is the same as '-')
p.negPre = '-' + p.posPre;
p.negSuf = p.posSuf;
}
return p;
}
function isString(value) { return typeof value === 'string'; }
function isNumber(value) { return typeof value === 'number'; }
/**
* Format a number into a string
* #param {number} number The number to format
* #param {{
* minFrac, // the minimum number of digits required in the fraction part of the number
* maxFrac, // the maximum number of digits required in the fraction part of the number
* gSize, // number of digits in each group of separated digits
* lgSize, // number of digits in the last group of digits before the decimal separator
* negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
* posPre, // the string to go in front of a positive number
* negSuf, // the string to go after a negative number (e.g. `)`)
* posSuf // the string to go after a positive number
* }} pattern
* #param {string} groupSep The string to separate groups of number (e.g. `,`)
* #param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
* #param {[type]} fractionSize The size of the fractional part of the number
* #return {string} The number formatted as a string
*/
function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
var isInfinity = !isFinite(number);
var isZero = false;
var numStr = Math.abs(number) + '',
formattedText = '',
parsedNumber;
if (isInfinity) {
formattedText = '\u221e';
} else {
parsedNumber = parse(numStr, '.');
roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
var digits = parsedNumber.d;
var integerLen = parsedNumber.i;
var exponent = parsedNumber.e;
var decimals = [];
isZero = digits.reduce(function (isZero, d) { return isZero && !d; }, true);
// pad zeros for small numbers
while (integerLen < 0) {
digits.unshift(0);
integerLen++;
}
// extract decimals digits
if (integerLen > 0) {
decimals = digits.splice(integerLen, digits.length);
} else {
decimals = digits;
digits = [0];
}
// format the integer digits with grouping separators
var groups = [];
if (digits.length >= pattern.lgSize) {
groups.unshift(digits.splice(-pattern.lgSize, digits.length).join(''));
}
while (digits.length > pattern.gSize) {
groups.unshift(digits.splice(-pattern.gSize, digits.length).join(''));
}
if (digits.length) {
groups.unshift(digits.join(''));
}
formattedText = groups.join(groupSep);
// append the decimal digits
if (decimals.length) {
formattedText += decimalSep + decimals.join('');
}
if (exponent) {
formattedText += 'e+' + exponent;
}
}
if (number < 0 && !isZero) {
return pattern.negPre + formattedText + pattern.negSuf;
} else {
return pattern.posPre + formattedText + pattern.posSuf;
}
}
function parse(numStr, decimalSep) {
var exponent = 0, digits, numberOfIntegerDigits;
var i, j, zeros;
DECIMAL_SEP = decimalSep;
// Decimal point?
if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
numStr = numStr.replace(DECIMAL_SEP, '');
}
// Exponential form?
if ((i = numStr.search(/e/i)) > 0) {
// Work out the exponent.
if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
numberOfIntegerDigits += +numStr.slice(i + 1);
numStr = numStr.substring(0, i);
} else if (numberOfIntegerDigits < 0) {
// There was no decimal point or exponent so it is an integer.
numberOfIntegerDigits = numStr.length;
}
// Count the number of leading zeros.
for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) {/* jshint noempty: false */ }
if (i === (zeros = numStr.length)) {
// The digits are all zero.
digits = [0];
numberOfIntegerDigits = 1;
} else {
// Count the number of trailing zeros
zeros--;
while (numStr.charAt(zeros) === ZERO_CHAR) zeros--;
// Trailing zeros are insignificant so ignore them
numberOfIntegerDigits -= i;
digits = [];
// Convert string to array of digits without leading/trailing zeros.
for (j = 0; i <= zeros; i++ , j++) {
digits[j] = +numStr.charAt(i);
}
}
// If the number overflows the maximum allowed digits then use an exponent.
if (numberOfIntegerDigits > MAX_DIGITS) {
digits = digits.splice(0, MAX_DIGITS - 1);
exponent = numberOfIntegerDigits - 1;
numberOfIntegerDigits = 1;
}
return { d: digits, e: exponent, i: numberOfIntegerDigits };
}
/**
* Round the parsed number to the specified number of decimal places
* This function changed the parsedNumber in-place
*/
function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
var digits = parsedNumber.d;
var fractionLen = digits.length - parsedNumber.i;
// determine fractionSize if it is not specified; `+fractionSize` converts it to a number
fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
// The index of the digit to where rounding is to occur
var roundAt = fractionSize + parsedNumber.i;
var digit = digits[roundAt];
if (roundAt > 0) {
// Drop fractional digits beyond `roundAt`
digits.splice(Math.max(parsedNumber.i, roundAt));
// Set non-fractional digits beyond `roundAt` to 0
for (var j = roundAt; j < digits.length; j++) {
digits[j] = 0;
}
} else {
// We rounded to zero so reset the parsedNumber
fractionLen = Math.max(0, fractionLen);
parsedNumber.i = 1;
digits.length = Math.max(1, roundAt = fractionSize + 1);
digits[0] = 0;
for (var i = 1; i < roundAt; i++) digits[i] = 0;
}
if (digit >= 5) {
if (roundAt - 1 < 0) {
for (var k = 0; k > roundAt; k--) {
digits.unshift(0);
parsedNumber.i++;
}
digits.unshift(1);
parsedNumber.i++;
} else {
digits[roundAt - 1]++;
}
}
// Pad out with zeros to get the required fraction length
for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
// Do any carrying, e.g. a digit was rounded up to 10
var carry = digits.reduceRight(function (carry, d, i, digits) {
d = d + carry;
digits[i] = d % 10;
return Math.floor(d / 10);
}, 0);
if (carry) {
digits.unshift(carry);
parsedNumber.i++;
}
}
})
$provide.decorator('currencyFilter', ['$delegate',
function ($delegate) {
var crncyFilter = $delegate;
var extendsFilter = function () {
var res = crncyFilter.apply(this, arguments);
if (arguments[2]) {
var digi1 = arguments[2] || 2;
return arguments[1] + Number(arguments[0]).toFixed(digi1);
}
else {
if (arguments[1] == "¥") {
return arguments[1] + Number(arguments[0]).toFixed(1);
}
}
};
return extendsFilter;
}]);
This is the way to Override Decimal digit
Here is a JS Fiddle.
The script speaks for itself. I just want to point out that it doesn't work. Please take a look and tell me what to do different. Thanks in advance. I have followed what I believe is every rule in javascript programming, but somewhere somehow I must have overlooked something. I have also made an actual working version og the script i PHP. The working PHP is the second script in this post: PHP split string at last number, insert an extra string and merge the new string.
function calTime(x) {
if (x === '') {
x = 54098;
} // Time in seconds
var f = 31536000, // seconds in a year
d = 86400, // seconds in a day
h = 3600, // seconds in an hour
m = 60, // seconds in a minute
xa,
xb,
xc,
xe,
xq,
string,
lb_y = 'year',
lb_ys = 'years',
lb_d = 'day',
lb_ds = 'days',
lb_h = 'hour',
lb_hs = 'hours',
lb_m = 'minute',
lb_ms = 'minutes',
lb_s = 'second',
lb_ss = 'seconds',
lb_and = 'and';
// a = years
var a = x / f;
// To prevent complications using scientific numbers less than 0 ex 7.2341232E-23
var a1 = a.indexOf("E-");
if (a1) {
a = 0;
}
// Split a so we only get the numbers before '.'
var a2 = a.indexOf(".");
if (a2) {
Math.floor(a);
}
// if $a is less or equal to 0 - it is 0
if (a <= 0) {
a = 0;
}
// b = days
var b = (x - (f * a)) / d;
// To prevent complications using scientific numbers less than 0 ex 7.2341232E-23
var b1 = b.indexOf("E-");
if (b1) {
b = 0;
}
// Split b so we only get the numbers before '.'
var b2 = b.indexOf(".");
if (b2) {
Math.floor(b);
}
// if $b is less or equal to 0 - it is 0
if (b <= 0) {
b = 0;
}
// c = hours
var c = (x - (f * a) - (d * b)) / h;
// To prevent complications using scientific numbers less than 0 ex 7.2341232E-23
var c1 = c.indexOf("E-");
if (c1) {
c = 0;
}
// Split c so we only get the numbers before '.'
var c2 = c.indexOf(".");
if (c2) {
Math.floor(c);
}
// if $c is less or equal to 0 - it is 0
if (c <= 0) {
c = 0;
}
// e = minutes
var e = (x - (f * a) - (d * b) - (h * c)) / m;
// Split $e so we only get the numbers before '.'
var e2 = e.indexOf(".");
if (e2) {
Math.floor(e);
}
// if $e is less or equal to 0 - it is 0
if (e <= 0) {
e = 0;
}
// $q = seconds
var q = (x - (f * a) - (d * b) - (h * c) - (m * e));
// Rewrite numbers if below 9
if (a <= 9) {
xa = '0' + a;
} else {
xa = a;
}
if (b <= 9) {
xb = '0' + b;
} else {
xb = b;
}
if (c <= 9) {
xc = '0' + c;
} else {
xc = c;
}
if (e <= 9) {
xe = '0' + e;
} else {
xe = e;
}
if (q <= 9) {
xq = '0' + q;
} else {
xq = q;
}
// Rewrite labels
if (a <= 1) {
lb_ys = lb_y;
}
if (b <= 1) {
lb_ds = lb_d;
}
if (c <= 1) {
lb_hs = lb_h;
}
if (e <= 1) {
lb_ms = lb_m;
}
if (q <= 1) {
lb_ss = lb_s;
}
// if == 0 - do not show
if (a === 0) {
a = '';
} else {
a = a + ' ' + lb_ys;
}
if (b === 0) {
b = '';
} else {
b = b + ' ' + lb_ds;
}
if (c === 0) {
c = '';
} else {
c = c + ' ' + lb_hs;
}
if (e === 0) {
e = '';
} else {
e = e + ' ' + lb_ms;
}
if (q === 0) {
q = '';
} else {
q = q + ' ' + lb_ss;
}
var time = [a, b, c, e, q];
time = time.filter(Number);
var count = time.count();
var last = time[time.length - 1];
if (count == 1) {
string = last;
} else if (count === 0) {
string = '<i>No Time described</i>';
} else {
string = time.join(', ') + ' ' + lb_and + ' ' + last;
}
return string;
}
document.getElementById("demo").innerHTML = calTime(83200);
I'll try to identify everything technically wrong with the script in one place.
Incorrect time calculations
There's 86400 seconds in a day and 31536000 seconds in a 365-day year. You'll usually see people do things like this if they want to not worry about values:
var minutes = 60;
var hours = 60 * 60;
var days = 24 * 60 * 60;
var years = 365 * 24 * 60 * 60;
Use of indexOf() on objects (in this case, numbers) that don't support that method.
Others have pointed this out in comments and answers, but basically, convert your numbers to strings if you're going to call string methods on them:
num = num + "";
numIndex = (num + "").indexOf("foo");
Not properly checking the return value of indexOf()
indexOf() returns the index (from 0) of the location where the string starts. If the string is not found, it returns -1. In several locations, you are doing something like:
var a2 = a.indexOf("E-");
if (a2) {
a = 0;
}
a2, in this case, will be -1 if it does not match scientific notation. The only integer value considered false is 0. The -1 value is therefore always considered true and you're always setting your years, days and hours to 0, regardless of whether they are in a scientific notation format or not.
Not considering case in the scientific notation format
In my browser, very small, close to zero values look like this:
7.888609052210118e-31
Your search won't match this value. This may not even matter, given your logic. Is there any reason just not to always use Math.floor()? Your JS floating-point problem is going to be a problem any way you slice it.
Not using the return value of Math.floor()
In several locations, you do something like the following:
Math.floor(a);
Then you go on to assume that a has assumed its floored value. You need to do the following to make that happen:
a = Math.floor(a);
Setting all of your time components to strings, and then filtering them by Number
You explicitly store string formats (e.g. 23 hours, 6 minutes) in your time array, but then, you filter the time array by Number. I think you're trying to filter out blank strings, which is what you set the time values to when they're 0. Pass a function to filter() to filter those blank entries out, like so:
time.filter(function(x) { return x === "" ? false : true; });
count() is not a method of Array
You're probably looking for length, which you actually use correctly below that.
You join the entire time array together, then tack on the last item in the array again
I'll let you solve this. You don't want to duplicate the last item, and you probably also want to handle the case where the time evenly divides into one of your categories
Those are the technical things wrong with your script. Another complete answer could be written on more elegant ways in which to accomplish what you're trying to do.
To add to the answers already given, the main style problem with your code is that there is a lot of unnecessary repetition, which you could get rid of using a loop and/or function.
Having said that, the maths can be greatly simplified, and there doesn't seem to be any need for all the stuff that searches for 'E' or '.', in which case it's probably not worth using a loop; in the suggestion below I use a loop only to add the labels. Another hint is to use descriptive variable names (hours, minutes, instead of a, b) where possible, to make your code more readable.
http://jsfiddle.net/m54Du/16/
function calTime(seconds) {
if (seconds === '') {
seconds = 54098;
} // Time in seconds
seconds = Math.floor(seconds);
if (isNaN(seconds) || seconds <= 0) {
return '<i>No time described</i>';
}
var minutes = Math.floor(seconds / 60),
hours = Math.floor(minutes / 60),
days = Math.floor(hours / 24),
years = Math.floor(days / 365), // assuming not leap!
timeData = [years, days % 365, hours % 24, minutes % 60, seconds % 60],
pluralLabels = ['years', 'days', 'hours', 'minutes' , 'seconds'],
singularLabels = ['year', 'day', 'hour', 'minute', 'second'],
time = [];
for (var i = 0; i < timeData.length; i++) {
if (timeData[i] > 1) {
time.push(timeData[i] + ' ' + pluralLabels[i]);
}
else if (timeData[i] > 0) {
time.push(timeData[i] + ' ' + singularLabels[i]);
}
}
var last = time.pop();
return time.length ? time.join(', ') + ' and ' + last : last;
}
document.getElementById("demo").innerHTML = calTime(83200);
Here's an alternative making more use of a loop to do the maths.
Your variables a, b, etc. are numbers. indexOf is a method for strings in JavaScript.
One way of fixing this would be to turn your numbers into strings, like:
a = a + "";
which removes the indexOf() errors.
For the rest of the errors, I think you're also misusing functions. For example, you use a count() method, which doesn't appear to exist in JavaScript.
I need to a function to convert an integer to the equivalent alpha ordered list index. For example:
1 = a
2 = b
.
.
.
26 = z
27 = aa
28 = ab
.
.
etc.
Currently I have the following which almost works but there's a small logic error somewhere that makes it not quite get it right (it goes ax, ay, bz, ba, bb, bc...):
function intToAlpha( int ) {
var asciiStart = 97,
alphaMax = 26,
asciiCode,
char,
alpha = '',
place,
num,
i;
for ( i = 0; Math.pow(alphaMax, i) < int; i++ ) {
place = Math.pow(alphaMax, i);
num = Math.floor( ( int / place ) % alphaMax);
asciiCode = ( num == 0 ? alphaMax : num ) + asciiStart - 1;
char = String.fromCharCode(asciiCode);
alpha = char + alpha;
}
return alpha;
}
for (i = 1; i < 300; i++) {
console.log( i + ': ' + intToAlpha(i) );
}
This function is used in NVu/Kompozer/SeaMonkey Composer, with a small tweak to generate lower case directly:
function ConvertArabicToLetters(num)
{
var letters = "";
while (num > 0) {
num--;
letters = String.fromCharCode(97 + (num % 26)) + letters;
num = Math.floor(num / 26);
}
return letters;
}
You need to make sure that you use the correct value when taking the mod.
function intToAlpha( int ) {
var asciiStart = 97,
alphaMax = 26,
asciiCode,
char,
alpha = "";
while(int > 0) {
char = String.fromCharCode(asciiStart + ((int-1) % alphaMax));
alpha = char + alpha;
int = Math.floor((int-1)/26);
}
return alpha;
}
A while back I needed the same thing in SQL, so I asked (and answered) the question Multi-base conversion - using all combinations for URL shortener.
The thing that is making it complicated is that it's not a straight base conversion, as there is no character representing the zero digit.
I converted the SQL function into Javascript:
function tinyEncode(id) {
var code, value, adder;
var chars = 'abcdefghijklmnopqrstuvwxyz';
if (id <= chars.length) {
code = chars.substr(id - 1, 1);
} else {
id--;
value = chars.length;
adder = 0;
while (id >= value * (chars.length + 1) + adder) {
adder += value;
value *= chars.length;
}
code = chars.substr(Math.floor((id - adder) / value) - 1, 1);
id = (id - adder) % value;
while (value > 1) {
value = Math.floor(value / chars.length);
code += chars.substr(Math.floor(id / value), 1);
id = id % value;
}
}
return code;
}
Demo: http://jsfiddle.net/Guffa/mstBe/
These are the logical steps which I need to do with jquery:
x is a 2 digit number(integer) derived from an input.value();
If var x is **not** 33 or 44
Convert this 2 digit number to string;
split the string in 2 parts as number;
Add these 2 values until they reduce to single digit;
Return var x value as this value;
Else
Return var x value literally as 33 or 44 whatever is the case;
Thanks!
if (x != 33 && x != 44) {
while (x > 9) {
var parts = ('' + x).split('');
x = parseInt(parts[0]) + parseInt(parts[1]);
}
return x;
} else {
return x;
}
Works only if the input is really max 2 digits long as you say, else you'll need to add the numbers in a for loop over parts.length. E.g.:
if (x != 33 && x != 44) {
while (x > 9) {
var parts = ('' + x).split('');
for (var x = 0, i = 0; i < parts.length; i++) {
x += parseInt(parts[i]);
}
}
return x;
} else {
return x;
}
I'd try:
function process (x) {
if ((x != 33) && (x != 44)) {
while (x > 9) {
x = Math.floor (x / 10) + (x % 10);
}
}
return x;
}
I see little reason to convert it to a string when you can use arithmetic operations.