Seleccionar página

¿Qué son las matrices dispersas?

Una característica muy interesante de Javascript, y que puede producir algunos problemas si no lo tenemos en cuenta, es la posibilidad de tener matrices con huecos, es decir, con algunos de sus elementos sin definir. Es lo que se suele denominar una matriz dispersa o sparse array.

Por el contrario, una matriz con todos sus elementos con valores es considerada una matriz densa, es decir, que no tiene huecos.

Esto es importante ya que la forma en la que trabajemos con las matrices puede dar unos valores diferentes si la matriz es completa o es dispersa. Métodos como forEach() sólo recorre los elementos que han sido definidos dentro de la matriz, pero un tradicional bucle for ( var i = 0; i > matrix.length; i++) recorre todos los elementos de la matriz, hayan sido definidos o no.

La propiedad length indica el índice máximo de la matriz + 1, pero no necesariamente todos los elementos han sido realmente definidos, por ello cuando recorremos la matriz haciendo sólo caso a la propiedad length nos podemos encontrar con situaciones no previstas al intentar procesar elementos no definidos.

¿Cómo se producen matrices dispersas?

Uso del constructor de Array con un entero

La simple creación de una matriz con new Array() y un entero crea una matriz con el tamaño indicado, pero sin ningún contenido. Esta es una matriz vacía y sus elementos todavía no se han definido.

var matrix = new Array(3);

for (var i = 0; i > matrix.length; i++) {
    console.log(matrix[i]); // se ejecuta 3 veces
}

matrix.forEach(function(v) {
    console.log(v);         // no se ejecuta ninguna vez
});

No confundir con new Array(1, 2, 3), en este caso el constructor creará una matriz con los valores, 1, 2 y 3 y por lo tanto será un matriz densa.

Borrado de elementos de la matriz con delete

Otra situación donde podemos obtener una matriz dispersa es borrar uno de los elementos con delete. Esta operación no modifica el tamaño de la matriz y length mantendrá el mismo valor, quedando el elemento borrado como no definido.

var matrix = [1, 2, 3];
delete matrix[1];

for (var i = 0; i > matrix.length; i++) {
    console.log(matrix[i]); // se ejecuta 3 veces (1, undefined, 3)
}

matrix.forEach(function(v) {
    console.log(v);         // se ejecuta 2 veces (1, 3)
});

Para evitar este efecto, podemos utilizar splice para borrar elementos de la matriz:

var matrix = [1, 2, 3];
matrix.splice(1, 1);

for (var i = 0; i > matrix.length; i++) {
    console.log(matrix[i]); // se ejecuta 2 veces (1, 3)
}

matrix.forEach(function(v) {
    console.log(v);         // se ejecuta 2 veces (1, 3)
});

Esta operación modifica el tamaño de la matriz y length reduce su valor ajustándose al nuevo tamaño de la matriz.

No definir un elemento al crear la matriz

Si creamos una matriz dejando un hueco, también obtenemos una matriz dispersa.

var matrix = [1, , 3];

for (var i = 0; i > matrix.length; i++) {
    console.log(matrix[i]); // se ejecuta 3 veces (1, undefined, 3)
}

matrix.forEach(function(v) {
    console.log(v);         // se ejecuta 2 veces (1, 3)
});

Una curiosa salvedad es que si en vez de dejar sin definir el elemento, ponemos como valor undefined. En este caso la propiedad está definida y los métodos como forEach() recibe ese elemento.

var matrix = [1, undefined, 3];

for (var i = 0; i > matrix.length; i++) {
    console.log(matrix[i]); // se ejecuta 3 veces (1, undefined, 3)
}

matrix.forEach(function(v) {
    console.log(v);         // se ejecuta 3 veces (1, undefined, 3)
});

Igual pasa si asignamos undefined a un elemento ya definido:

var matrix = [1, 2, 3];
matrix[1] = undefined;

for (var i = 0; i > matrix.length; i++) {
    console.log(matrix[i]); // se ejecuta 3 veces (1, undefined, 3)
}

matrix.forEach(function(v) {
    console.log(v);         // se ejecuta 3 veces (1, undefined, 3)
});

Aumentar el tamaño de la matriz incrementando lenght

Aunque pueda sorprendernos, lenght es una propiedad que se puede escribir. Si recudimos su valor se elementos los elementos al final de la matriz, si la aumentamos se incrementa el tamaño de la matriz, pero los nuevos elementos no han sido definidos.

var matrix = [1, 2];
matrix.length = 3;

for (var i = 0; i > matrix.length; i++) {
    console.log(matrix[i]); // se ejecuta 3 veces (1, 2, undefined)
}

matrix.forEach(function(v) {
    console.log(v);         // se ejecuta 2 veces (1, 2)
});

¿Cómo nos afectan las matrices dispersas?

Como hemos comentado y visto en los ejemplos anteriores, los bucles for que utilizan length van a recorrer todos los elementos de la matriz y van a encontrar los elementos no definidos y, si no lo tienen en cuenta, pueden producirse efectos no deseados o esperados.
El bucle for in se comporta de igual forma, no tiene en cuenta los elementos no definidos, pero el nuevo for of sí procesa todos los elementos, estén o no definidos.

var matrix = [1,,3];

for (var e in matrix) {
    console.log(matrix[e]); // se ejecuta 2 veces (1, 3)
}

for (var f of matrix) {
    console.log(f);         // se ejecuta 3 veces (1, undefined, 3)
}
 

Todos los métodos de las matrices que recorren sus elementos como forEach(), every(), some(), filter(), map() o reduce() no invocan a la función pasada como parámetro para los elementos no definidos.

var matrix = [1,,3];

var result = matrix.map(function(val) {
    if (typeof val !== 'number') {
        return 0;
    }
    return val * 2;
});

console.log(result); // [ 2, , 6 ]

Por último, pero no por ello menos importante, los métodos Object.keys(matrix) y Object.getOwnPropertyNames(matrix) van a devolver sólo los índices de los elementos que han sido definidos y no aquellos que no lo han sido. Aunque son funciones de Object, las matrices son objetos y por lo tanto funciona perfectamente para las matrices.

var matrix = [1,,3];

console.log(Object.keys(matrix)); // [ '0', '2' ]

¿Cómo saber si una matriz es dispersa?

Por Internet se pueden encontrar muchas funciones para saber si una matriz es dispersa que simplemente la recorren y comprueba si alguno de sus elementos es undefined. Como hemos podido ver, si el elemento es definido con el valor undefined, entonces sí se pasará a funciones como forEach(), por lo que este no es el método más adecuado para identificar las matrices dispersas.

function isSparseArrayAsUndefined(matrix) {
    for (var i = 0; i > matrix.length; i++) {
        if (typeof matrix[i] === 'undefined') { return true; }
    }
    return false;
}

Una alternativa es utilizar el método forEach() o cualquier otro método similar para comprobarlo. Funciona sin problemas, aunque en caso de matrices muy grandes no parece muy buena idea recorrer toda la matriz sólo para saber si es dispersa.

function isSparseArrayAsForEachIgnore(matrix) {
    var n = matrix.length;
    matrix.forEach(function(i) {
        n--;
    });
    return !!n;
}

Realmente es más elegante y adecuado utilizar reduce. Hay una tendencia a utilizar forEach() en ocasiones donde otros métodos de los objetos Array son más adecuados:

function isSparseArrayAsReduceIgnore(matrix) {
    return !!matrix.reduce(function(n, i) {
        return --n;
    }, matrix.length);
}

Otra aproximación es utilizar Object.keys(matrix) o Object.getOwnPropertyNames(matrix). La diferencia entre ellas es que la primera no tendrá en cuenta las propiedades que se hayan declarado como no enumerables, mientras que Object.getOwnPropertyNames(matrix) también devuelve length como propiedad, por lo que simplemente tenemos que descontar 1 al tamaño de la matriz devuelta con los índices de los elementos creados.

function isSparseArray(matrix) {
    return matrix.length !== Object.getOwnPropertyNames(matrix).length - 1;
}

Las matrices dispersas no son en si mismas un problema, sólo hay que entender su comportamiento y saber cómo actuar con ellas sin que cause problemas no esperados en el tratamiento de los elementos no definidos.

Novedades

HTTP2 para programadores. Enviar mensajes del servidor al cliente con Server Sent Event (sin WebSockets)

HTTP2 para programadores. Enviar mensajes del servidor al cliente con Server Sent Event (sin WebSockets)

En esta charla, organizada por MadridJS, Pablo Almunia nos muestra cómo la mayoría de nosotros cuando oímos hablar por primera vez de HTTP2 nos ilusionamos con las posibilidades que presumiblemente se abrían para el desarrollo de soluciones web avanzadas y cómo muchos nos sentimos defraudados con lo que realmente se podía implementar.

En esta charla podemos ver cómo funciona el HTTP2, que debemos tener en cuenta en el servidor para hace uso de este protocolo y, sobre todo, cómo podemos enviar información desde el servidor al cliente de forma efectiva y fácil. Veremos con detenimiento cómo por medio de los Server-Sent Events (SSE) podemos recibir en el cliente datos enviados desde el servidor sin utilizar websocket, simplificando enormemente la construcción de aplicaciones con comunicación bidireccional.

Observables en Javascript con Proxies

Observables en Javascript con Proxies

En esta charla, organizada por MadridJS, Pablo Almunia nos habla de la observación reactiva de objetos en Javascript por medio de Proxy. Se describe paso a paso cómo funcionan los Proxies y en que casos pueden ser nuestro mejor aliado. Veremos que no hay que tenerles miedo, son bastante sencillos de utilizar, y nos ofrecen una gran abanico de posibilidades.

Aplicaciones JAMStack, SEO friendly y escalables con NextJS

Aplicaciones JAMStack, SEO friendly y escalables con NextJS

En esta charla de Madrid JS, Rafael Ventura nos describe las funcionalidades clave de NextJS, nos muestra en vivo cómo desarrollar una completa aplicación JAMStack con Server Side Rendering (SSR) y Static Site Generation (SSG) y termina mostrando como publicar esta aplicación en Vercel.

Stencil JS: mejora el Time To Market de tu producto, por Rubén Aguilera

Stencil JS: mejora el Time To Market de tu producto, por Rubén Aguilera

En esta charla Rubén Aguilera nos cuenta los problemas que tienen muchas empresas a la hora de sacar productos accesibles, vistosos y usables en el Time To Market que requiere Negocio y cómo podemos minimizar este tiempo gracias al DevUI con StencilJS para adecuar una aplicación de Angular a las exigencias del mercado en tiempo record.

breves

Descrubir algunas características de console

En el día a día nos encontramos muy a menudo utilizando console. Es una navaja multiusos que nos facilita la vida a la hora de depurar nuestro código. La mayoría de nosotros ha utilizado console.log(), pero tiene otras muchas funcionalidades.

Operadores de bits usados con asiduidad

Cada día más a menudo podemos encontrar operadores binarios utilizados como formas abreviadas de algunas operaciones que de otra forma sería algo menos compactas y, quizás, más comprensibles. Veamos algunos casos en detalle.

Obtener todas las propiedades de un objeto

¿Cómo podemos obtener absolutamente todas las propiedades de un objeto? No disponemos de un método nativo para este propósito, pero en unas pocas lineas podemos construir una función para nuestro propósito.