Disminución de la ID de Tweet de 64 bits en JavaScript

Como algunos de ustedes sabrán, JavaScript es solo Capaz de manejar números enteros de hasta 53 bits de tamaño. Esta publicación, Trabajar con números enteros grandes en JavaScript (que es parte de la serie Números) hace un gran trabajo al explicar los conceptos generales sobre el manejo de números grandes en JS.

El ID de Tweet de 64 bits está "redondeado" en JS

Tuve que investigar un poco sobre el tema cuando estaba reescribiendo un código JavaScript responsable de manejar la búsqueda de Twitter en el editor de Storify:¡teníamos tweets duplicados en los resultados! En este artículo, Trabajar con líneas de tiempo, la documentación oficial de Twitter dice:

Tan cierto, porque id y id_str los campos en una respuesta API de Twitter eran diferentes. Aparentemente, el motor de JavaScript simplemente "redondea" números inapropiadamente grandes. :-( La tarea se complicó por el hecho de que necesitaba restar 1 de la ID del último tweet para evitar que volviera a aparecer en una segunda respuesta de búsqueda. Después de la resta, podría haber pasado fácilmente el valor a max_id parámetro de la API de Twitter.

Me encontré con diferentes soluciones, pero decidí escribir mi propia función, que es simple de entender y no requiere muchos recursos. Aquí hay una secuencia de comandos para disminuir la ID del tweet, que es un número de 64 bits en JavaScript sin bibliotecas ni recursividad, para usar con max_id o since_id en la API de Twitter:

function decStrNum (n) {
    n = n.toString();
    var result=n;
    var i=n.length-1;
    while (i>-1) {
      if (n[i]==="0") {
        result=result.substring(0,i)+"9"+result.substring(i+1);
        i --;
      }
      else {
        result=result.substring(0,i)+(parseInt(n[i],10)-1).toString()+result.substring(i+1);
        return result;
      }
    }
    return result;
}

Para comprobar si funciona, puede ejecutar estos registros:

console.log("290904187124985850");
console.log(decStrNum("290904187124985850"));
console.log("290904187124985851");
console.log(decStrNum("290904187124985851"));
console.log("290904187124985800");
console.log(decStrNum("290904187124985800"));
console.log("000000000000000001");
console.log(decStrNum("0000000000000000001"));

Bob Lauer sugirió una solución alternativa que encontré en una pregunta de StackOverflow, pero implica recursión y en mi humilde opinión es más complicado:

function decrementHugeNumberBy1(n) {
    // make sure s is a string, as we can't do math on numbers over a certain size
    n = n.toString();
    var allButLast = n.substr(0, n.length - 1);
    var lastNumber = n.substr(n.length - 1);

    if (lastNumber === "0") {
        return decrementHugeNumberBy1(allButLast) + "9";
    }
    else {      
        var finalResult = allButLast + (parseInt(lastNumber, 10) - 1).toString();
        return trimLeft(finalResult, "0");
    }
}

function trimLeft(s, c) {
    var i = 0;
    while (i < s.length && s[i] === c) {
        i++;
    }

    return s.substring(i);
}

Ahora, si eres el tipo de persona a la que le gusta disparar gorriones con un obús , existen bibliotecas completas para manejar operaciones con grandes números en JavaScript; solo por nombrar algunos:BigInteger, js-numbers y javascript-bignum.