Optimización de listas largas de valores Sí/No con JavaScript

 

 

 

  • SmashingConf Nueva York 2024
  • ¡Registro!

  • Índice
    1. Usando una matriz
    2. Usando una cuerda
    3. Usando campos de bits
    4. ¿Podemos hacerlo mejor?
    5. Empaquetando 16 valores en un solo carácter
    6. Limitaciones
    7. "Creo que lo llevaste demasiado lejos"
      1. Otras lecturas

    ¿Necesita almacenar una larga lista de valores booleanos en algo que sólo acepte cadenas? ¡Lea este artículo de Lea Veroud!

     

    Muy frecuentemente en el desarrollo web (y en la programación en general), es necesario almacenar una larga lista de valores booleanos (sí/no, verdadero/falso, marcado/no marcado... ya entiendes la idea) en algo que acepte sólo cadenas. Tal vez sea porque desea almacenarlos en localStorageuna cookie, o enviarlos a través del cuerpo de una solicitud HTTP. He necesitado hacer esto innumerables veces.

    La última vez que me topé con un caso así no fue con mi propio código. Fue entonces cuando Christian Heilmann me mostró su nuevo conjunto de diapositivas, con una función interesante que permitía alternar la visibilidad de diapositivas individuales dentro y fuera de la presentación. Al verlo quedé impresionado. Sin embargo, mirando más de cerca, me di cuenta de que los estados de las casillas de verificación no persistían después de recargar la página.

    Entonces, alguien podría pasar mucho tiempo ajustando cuidadosamente sus diapositivas, solo para presionar accidentalmente F5 o bloquear su navegador, y luego... ¡boom! – todo su trabajo se perdería. Christian me dijo que ya estaba trabajando en almacenar los estados de las casillas de verificación en formato localStorage. Luego, naturalmente, debatimos interminablemente sobre el formato de almacenamiento. Ese debate me inspiró a escribir este artículo para explorar en profundidad los distintos enfoques.

    Usando una matriz

    Tenemos dos formas (razonables) de modelar nuestros datos en una matriz. Una es almacenar valores verdadero/falso, así:

     

    [false, true, true, false, false, true, true]

    La otra es almacenar una matriz de 0 y 1, así:

    [0, 1, 1, 0, 0, 1, 1]

    Cualquiera que sea la solución que elijamos, en última instancia tendremos que convertirla en una cadena y luego volver a convertirla en una matriz cuando se lea. Tenemos dos formas de proceder: o con el antiguo Array#join()(o Array#toString()) y String#split(), o con el más sofisticado JSON.stringify()y JSON.parse().

    Con el modo JSON, el código será algo más corto, aunque es el equivalente en JavaScript a cortar pan con una motosierra. No sólo hay un impacto en el rendimiento en la mayoría de los navegadores , sino que también se está reduciendo bastante el soporte del navegador.

    El principal inconveniente de utilizar cadenas basadas en matrices es su tamaño en bytes. Si opta por el método numérico, utilizará casi 2 caracteres por número (o, más precisamente, 2N − 1ya que necesitaría un delimitador por número, excepto el último):

    [0, 1, 1, 0, 0, 1, 1].toString().length // 13, for 7 values

    Entonces, para 512 números, serían 1023 caracteres o 2 KB, ya que JavaScript usa UTF-16 . Si optas por el método booleano, es aún peor:

    [false, true, true, false, false, true, true].toString().length // 37, also for 7 values

    Eso es alrededor de 5 a 6 caracteres por valor, es decir, de 2560 a 3072 caracteres para 512 números (que son de 5 a 6 KB). JSON.stringify()incluso desperdicia 2 caracteres más en cada caso, para los corchetes de apertura y cierre, pero su ventaja es que recupera sus tipos de valores originales JSON.parse()en lugar de cadenas.

    Usando una cuerda

    El uso de una cadena ahorra algo de espacio, porque no hay delimitadores involucrados. Por ejemplo, si opta por el enfoque numérico y almacena cadenas como ‘01001101010111’, esencialmente está almacenando un carácter por valor, lo cual es 100 % mejor que el mejor de los dos enfoques anteriores. Luego puede obtener los valores en una matriz usando String#split:

    '01001101010111'.split(’); // ['0','1','0','0','1','1','0','1','0','1','0','1','1','1']

    O simplemente puede recorrer la cadena usando string.charAt(i), o incluso los índices de cadena ( string[i]), si no le importan los navegadores más antiguos.

    Usando campos de bits

    ¿El método anterior te hizo pensar en números binarios? No eres sólo tú. El concepto de campos de bits es bastante popular en otros lenguajes de programación, pero no tanto en JavaScript. En pocas palabras, los campos de bits se utilizan para empaquetar muchos valores booleanos en los bits de la representación booleana de un número. Por ejemplo, si tiene ocho valores (verdadero, falso, falso, verdadero, falso, verdadero, verdadero, falso), el número sería 10010110 en binario; entonces, 150 en decimal y 96 en hexadecimal. Son 2 caracteres en lugar de 8, por lo que se ahorra un 75% . En general, 1 dígito en la representación hexadecimal corresponde exactamente a 4 bits. (Eso es porque ... En general, en un sistema, puedes empaquetar bits en cada dígito). Entonces, no tuvimos suerte con ese 75%; siempre es tanto .16 = 24base2nnbase2n Mejores pianos digitales

     

    Por lo tanto, en lugar de almacenar esa cadena como una cadena y usar 1 carácter por valor, podemos ser más inteligentes y convertirla primero en un número (hexadecimal). ¿Como hacemos eso? No es más que una línea de código:

    parseInt('10010110', 2).toString(16); // returns '96'

    ¿Y cómo lo leemos de nuevo? Eso es igual de simple:

    parseInt('96', 16).toString(2); // returns '10010110'

    A partir de este punto, podemos seguir el mismo proceso que el método anterior para recorrer los valores y hacer algo útil con ellos.

    ¿Podemos hacerlo mejor?

    De hecho, ¡podemos! ¿Por qué convertirlo a un número hexadecimal (base 16), que utiliza sólo 6 de las 26 letras del alfabeto? El Number#toString()método nos permite subir a la base 36 (lanzando a RangeErrorfor = 37), que efectivamente usa todas las letras del alfabeto, ¡hasta z! De esta manera, podemos tener una compresión de hasta 6 caracteres para 32 valores, lo que significa un ahorro de hasta un 81,25% en comparación con el método de cadena simple. Y el código es igual de simple:

    parseInt( '1001011000', 2).toString(36); // returns 'go' (instead of '258', which would be the hex version)parseInt('go', 36).toString(2); // returns '1001011000'

    Para algunos de ustedes esto será suficiente. Pero casi puedo escuchar a las mentes más curiosas gritar: "¡Pero tenemos letras mayúsculas, tenemos otros símbolos, todavía no estamos usando las cuerdas en todo su potencial!" Y estarías en lo cierto. Hay una razón por la cual cada vez que abres un archivo binario en un editor de texto, obtienes símbolos extraños mezclados con números, letras mayúsculas, minúsculas y todo eso. Cada carácter en una cadena UTF-16 tiene 2 bytes (16 bits), lo que significa que si usamos el algoritmo de compresión correcto, deberíamos poder almacenar 16 valores de sí/no en ella (ahorrando un 93,75% del método de cadena). .

    El problema es que JavaScript no ofrece una forma integrada de hacerlo, por lo que el código se vuelve un poco más complicado.

    Empaquetando 16 valores en un solo carácter

    Puedes usarlo String.fromCharCodepara obtener los personajes individuales. Acepta un valor numérico de hasta 65.535 y devuelve un carácter (y para valores mayores que ese, devuelve una cadena vacía).

    Entonces, tenemos que dividir nuestra cadena en trozos de 16 caracteres. Podemos hacerlo a través de.match(/.{1,16}/g) . En resumen, la solución completa se vería así:

    function pack(/* string */ values) { var chunks = values.match(/.{1,16}/g), packed = ’; for (var i=0; i chunks.length; i++) { packed += String.fromCharCode(parseInt(chunks[i], 2)); } return packed;}function unpack(/* string */ packed) { var values = ’; for (var i=0; i packed.length; i++) { values += packed.charCodeAt(i).toString(2); } return values;}

    No fue tan difícil, ¿verdad?

    Con estas pocas líneas de código, puede empaquetar los 512 valores antes mencionados en (redoble de tambores, por favor) ¡ 32 caracteres (64 bytes) !

    Una gran mejora con respecto a nuestros 2 KB originales (con el método de matriz), ¿no es así?

    Limitaciones

    Los números en JavaScript tienen límites. Para los métodos discutidos aquí que implican un estado intermedio de conversión a un número, el límite parece ser 1023 valores sí/no, porque parseInt(‘1111…1111’, 2)regresa Infinitycuando el número de ases es mayor que 1023. Este límite no se aplica al último método, porque sólo estamos convirtiendo bloques de bits en lugar de todo. Y, por supuesto, no se aplica a los dos primeros métodos (matriz y cadena) porque no implican empaquetar los valores en un número entero.

    "Creo que lo llevaste demasiado lejos"

    Esto podría resultar excesivo en algunos casos. Pero definitivamente será útil cuando desee almacenar muchos valores booleanos en un espacio limitado que solo pueda almacenar cadenas. Y ninguna optimización es excesiva para las cosas que pasan por el cable con frecuencia. Por ejemplo, las cookies se envían con cada solicitud, por lo que deben ser lo más pequeñas posible. Otro caso de uso serían los juegos multijugador online, para los cuales los tiempos de respuesta deberían ser ultrarrápidos, de lo contrario los juegos no serían divertidos.

    E incluso si este tipo de optimización no es lo tuyo, espero que hayas encontrado educativo el proceso de pensamiento y el código involucrado.

    Gracias a Eli Gray y Jonas Wagner por sus consejos y correcciones.

    Imagen de portada creada por Ruiwen Chua .

    Otras lecturas

    • 7 cosas de JavaScript que desearía saber mucho antes en mi carrera
    • Una mirada rápida a las matemáticas de las animaciones con JavaScript
    • 10 rarezas y secretos sobre JavaScript

    (al, señor)Explora más en

    • Codificación
    • javascript
    • Técnicas





    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Optimización de listas largas de valores Sí/No con JavaScript

    Optimización de listas largas de valores Sí/No con JavaScript

    SmashingConf Nueva York 2024 ¡Registro! Índice Usando una matriz

    programar

    es

    https://pseint.es/static/images/programar-optimizacion-de-listas-largas-de-valores-sino-con-javascript-783-0.jpg

    2024-04-04

     

    Optimización de listas largas de valores Sí/No con JavaScript
    Optimización de listas largas de valores Sí/No con JavaScript

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20