最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Increment an integer string - Stack Overflow

programmeradmin0浏览0评论

I was recently asked to increment an integer string in Javascript as part of an interview. I managed to do it, but my code was quite messy. What is a good algorithm for incrementing integer strings by 1 in Javascript (or any language)?

"1"=>"2"
"9"=>"10"
"-10"=>"-9"
"-1"=>"0"
"123456"=>"123457"

This is to prevent integer overflow, so obviously I cannot convert the string to an integer.

Whoever es up with a solution, please test it with the following code (assuming your function is called inc):

var s = '-1000';
for(var i = -999; i <= 999; i++) {
    s = inc(s);
    if(s !== i.toString())
        throw [s, i];
}

I was recently asked to increment an integer string in Javascript as part of an interview. I managed to do it, but my code was quite messy. What is a good algorithm for incrementing integer strings by 1 in Javascript (or any language)?

"1"=>"2"
"9"=>"10"
"-10"=>"-9"
"-1"=>"0"
"123456"=>"123457"

This is to prevent integer overflow, so obviously I cannot convert the string to an integer.

Whoever es up with a solution, please test it with the following code (assuming your function is called inc):

var s = '-1000';
for(var i = -999; i <= 999; i++) {
    s = inc(s);
    if(s !== i.toString())
        throw [s, i];
}
Share Improve this question edited Feb 4, 2015 at 23:52 georg 215k56 gold badges322 silver badges399 bronze badges asked Feb 4, 2015 at 22:40 Leo JiangLeo Jiang 26.2k59 gold badges176 silver badges327 bronze badges 3
  • 4 You're dealing with integers larger than 9007199254740992 and incrementing them by one? (That would be my first response. "Are you SURE you need this?" :) – Phrogz Commented Feb 4, 2015 at 22:47
  • @Phrogz Interview problems are generally a small part of a bigger puzzle. – sarveshseri Commented Feb 4, 2015 at 23:03
  • I took liberty to add testing code to your question, because many answers so far don't pass it. – georg Commented Feb 4, 2015 at 23:54
Add a ment  | 

7 Answers 7

Reset to default 3
  1. Split the string into substrings that are definitely small enough to increment properly.
  2. Increment/decrement the last chunk (based on the sign).
  3. Carry any overflow into the next chunk—or borrow from the next chunk—until done.
  4. Join the integers as strings again.

Since you specifically asked not to convert strings to integers, here is a method that uses only string manipulation function - no parseInt.

To use it just call incrementString('123').

var incrementMapPositive = { '0': '1', '1': '2', '2': '3', '3': '4', '4': '5', '5': '6', '6': '7', '7': '8', '8': '9', '9': '0' };
var incrementMapNegative = { '0': '9', '9': '8', '8': '7', '7': '6', '6': '5', '5': '4', '4': '3', '3': '2', '2': '1', '1': '0' };

function incrementString(str, pos) {
    if (!str)
        return '';

    // handle case '-0'
    if (/^-0+$/.test(str))
        str = '0';

    // position count starts from the end, so get the proper index
    var posFromStart = str.length - 1 - (pos || 0);

    // get the character to increment (use the last one if none specified)
    var chr = str.charAt(posFromStart);
    var isNegative = str.charAt(0) === '-';
    var incrementMap = isNegative ? incrementMapNegative : incrementMapPositive;

    // if the very first digit is carried (e.g. incrementing from 99 to 100), we need to add a '1'
    var newChr = !chr ? '1' : incrementMap[chr];

    // build the new string with the replaced digit
    var incremented = str.substr(0, posFromStart) + newChr + str.substr(posFromStart + 1);

    // detect if we need to carry/borrow from the next digit
    if (!isNegative && newChr === '0' || isNegative && newChr === '9')
        incremented = incrementString(incremented, (pos || 0) + 1);

    //clean leading zeros
    incremented = incremented.replace(/^(-)?0+/, '$1');
    if (incremented === '-')
        incremented = '0';

    return incremented;
}

EDIT: replaced if-else blocks with a mapping object. Also incrementing correctly negative numbers now.

EDIT 2: made some cleanup and added some inline ments to better explain code.

EDIT 3: handling case '-0'

To handle carrying correctly, you could use something like:

function replaceChar(str, index, character) {
  return str.substr(0, index) + character + str.substr(index + character.length);
}

function incStr(str) {
  var carry = false;
  var ret = str;
  for (var i = str.length; i > 0; --i) {
    var inc = str.charCodeAt(i - 1);
    if (i === str.length) {
      ++inc;
    }
    if (carry) {
      ++inc;
    }
    if (inc > 58) {
      carry = true;
      ret = replaceChar(ret, i - 1, "1");
    } else if (inc === 58) {
      carry = true;
      ret = replaceChar(ret, i - 1, "0");
    } else {
      carry = false;
      ret = replaceChar(ret, i - 1, String.fromCharCode(inc));
    }
  };
  if (carry) {
    return "1" + ret;
  } else {
    return ret;
  }
}

var testData = ["13", "14", "19", "20", "300", "08974871238471923501", "999999999999999999999999999"];

var results = testData.map(function(it) {
  return incStr(it);
});

document.getElementById("results").textContent = JSON.stringify(results);
<pre id="results"></pre>

It's definitely not the prettiest implementation, as it creates a lot of strings along the way (at least one for each digit in the input), but it will run with linear speed (depending on the input length) and won't balloon in memory for large values.

There is not a single actual string-to-number conversion, as we know that the ten valid digits ([0-9]) e in order, and we can simply increment the character code. If it goes beyond 9 (followed by :), then we check:

  1. If it's greater than '9'+1, we're carrying and hit the initial increment, so the digit should be one and we carry.
  2. If it's greater than '9', we're carrying, so the digit should be zero and we carry.
  3. Otherwise, we didn't wrap, so replace the digit but don't carry.

This code works for both positive and negative numbers - nothing simple, but self explanatory. Good luck with the job..

    var tests = ['9007199254740992','999999999999999999','-999999999','-0','-80','800','99']
function addOne(num){


    if (Math.abs(parseInt(num,10)) == 0){return '1'};

    var numArray = num.split(''),
        answer   = [];
    var sign = 1;
    if (numArray[0] == '+' || numArray[0] == '-'){
        if (numArray[0] == '-'){sign = -1};
        numArray.splice(0,1);
    }
    var idx = numArray.length;
    var next_int = 0;
    var carry = 1; /* first add 1 */
    while(idx-- && carry != 0){
        next_int = parseInt(numArray[idx],10) + (sign * carry);
        if (sign > 0){
            answer[idx] = (next_int >= 10 ? next_int - 10 : next_int)
            carry = (next_int >= 10 ? 1 : 0)
        } else {
            answer[idx] = (next_int == -1 ? 9 : Math.abs(next_int));
            carry = (next_int == -1 ? 1 : 0)
        }
        if (carry == 0){
            while(idx--){
                answer[idx] = numArray[idx]
            }

        }

    }

    if (sign > 0){
        return (carry == 0 ? answer.join('') : carry + answer.join(''));
    } else {
        return ('-' + answer.join(''));
    }
}

for (var i = 0;i<tests.length;i++){
    console.log (' Num In: ' + tests[i] + ' Num Out: ' + addOne(tests[i]));
}


Num In: 9007199254740992 Num Out: 9007199254740993
Num In: 999999999999999999 Num Out: 1000000000000000000 /* bigger than big */
Num In: -999999999 Num Out: -999999998
Num In: -0 Num Out: 1
Num In: -80 Num Out: -79
Num In: 800 Num Out: 801
Num In: 99 Num Out: 100

Lets look at the last value which has precision as a Number in JavaScript when added to.

var s = ""+Number.MAX_SAFE_INTEGER;

And add one to it.

s++;//"9007199254740992"

Now the next increment will fail to add value. In order to increment, there needs to be some tweak involved. The benefit is that incrementing by 1 allows some basic assumptions to be made. Basically which is no number to the left of 9 has a chance of being changed.

//defined
function inc(num,i){
 i = i == undefined ? 1 : i;
 var c = num[num.length-i];
 var left = num.substr(0,num.length-i);
 var right = num.substr(num.length-i+1);
 if( c == 9 ){
  return inc(left+0+right,i+1);
 }else{
  return left+(+c+1)+right;
 }
}

//used
var s = ""+(Number.MAX_SAFE_INTEGER+1);//"9007199254740992"
for(var i = 0; i < 10; i++){
 s = inc(s);
}
alert(s);//"9007199254741002"

My strategy

  • Put each digit as a integer in an array
  • increment right hand side by one
  • work backwards carrying
  • add a 1 if overflow at left hand side
  • for negatives model as subtraction i.e. -2 + 1 is actual -(2 - 1)

Code

function inc(str) {
    var parts = [];
    var i = 0;
    var negative = str[0] === '-';
    for (i = negative ? 1 : 0; i < str.length; i++) {
         parts.push(str.charCodeAt(i) - 48);
    }
    parts[parts.length - 1] += (negative ? -1 : 1);

    for (i = parts.length - 1; i >= 0; i--) {  // carry right to left
        if (parts[i] > 9) { // overflow
            parts[i] = 0;
            if (i > 0) {
                parts[i - 1]++;
            } else {
                parts.unshift(1); // 99 -> 100 case
            }
        } else if (parts[i] < 0) { // underflow
            parts[i] = 9;
            if (i > 0) {
                parts[i - 1]--;
            } else {
                parts.shift();  // 100 -> 99 case
            } 
        }
    }
    if (negative) {
        parts.unshift("-");
    }
    return parts.join('');
}

My approach would be a maybe memory-intensive one using array methods:

var num = '12345';

var newNum = num.split('').map(function(el){

    return ''+(++el);

}).join('');

EDIT

forgot to think about carry - moment please^^

var num = '278889';
var newNum;

var temp = num.split('').reverse().reduce(function(carry, el){

  // carry[0] holds the carry from last operation, carry[1] is our new num
  // ~~num is same as Math.float() or in this case paseInt
  ++el;
  el += carry[0];
  return [ ~~(el / 10), (el % 10) + '' + carry[1] ];
  
}, [0,'']);

// We have to check if last carry was zero or not
newNum = (temp[0] != 0 ? temp[0] : '') + temp[1];

// outputs ["390000"]
console.log([newNum]);

发布评论

评论列表(0)

  1. 暂无评论