Combinando matrices de JavaScript -

PorKyle Simpson es

Esta es una publicación rápida y sencilla sobre técnicas de JavaScript. Vamos a cubrir diferentes métodos para combinar/fusionar dos matrices JS y los pros y los contras de cada enfoque.

Comenzamos con el escenario:

var a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ];var b = [ "foo", "bar", "baz", "bam", "bun", "divertido" ];

La concatenación simple de ay bsería, obviamente,:

[1, 2, 3, 4, 5, 6, 7, 8, 9, "foo", "bar", "baz", "bam" "bun", "divertido"]
Índice de contenidos
  1. concat(..)
  2. Inserción en bucle
  3. Trucos funcionales
  4. Summary
    1. Acerca de Kyle Simpson

concat(..)

El enfoque más común es:

var c = a.concat( b );a; // [1,2,3,4,5,6,7,8,9]b; // ["foo","bar","baz","bam","bun","diversión"]c; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","divertido"]

Como puedes ver, ces completamente nuevo arrayy representa la combinación de los dos ay blas matrices, dejándolo aintacto b. Sencillo, ¿verdad?

¿Qué pasa si ason 10.000 artículos y bson 10.000 artículos? cAhora hay 20.000 elementos, lo que básicamente equivale a duplicar el uso de memoria de ay b.

“¡No hay problema!”, dice. Simplemente los desarmamos ay bpor eso se recolectan basura, ¿verdad? ¡Problema resuelto!

a = b = nulo; // `a` y `b` pueden desaparecer ahora

Bueno. Por sólo un par de arrays pequeños, esto está bien. Pero para arrayarchivos grandes, o para repetir este proceso regularmente muchas veces, o para trabajar en entornos con memoria limitada, deja mucho que desear.

Inserción en bucle

Bien, agregamos arrayel contenido de uno al otro, usando Array#push(..):

// `b` sobre `a`for (var i=0; i b.length; i++) { a.push( b[i] );}a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]b = null;

Ahora, atiene el resultado tanto del original acomo del contenido de b.

Parecería que es mejor para la memoria.

Pero, ¿y si afuera pequeño y bcomparativamente muy grande? Por razones de memoria y velocidad, probablemente desee colocar el más pequeño aen el frente ben lugar del más largo ben el final a. No hay problema, simplemente reemplaza push(..)con unshift(..)y haz un bucle en la dirección opuesta:

// `a` en `b`:for (var i=a.length-1; i = 0; i--) { b.unshift( a[i] );}b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]a = null;

Trucos funcionales

Unfortunately, for loops are ugly and harder to maintain. Can we do any better?

Here’s our first attempt, using Array#reduce:

// `b` onto `a`:a = b.reduce( function(coll,item){    coll.push( item );    return coll;}, a );a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]// or `a` into `b`:b = a.reduceRight( function(coll,item){    coll.unshift( item );    return coll;}, b );b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]

Array#reduce(..) and Array#reduceRight(..) are nice, but they are a tad clunky. ES6 = arrow-functions will slim them down slightly, but it’s still requiring a function-per-item call, which is unfortunate.

What about:

// `b` onto `a`:a.push.apply( a, b );a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]// or `a` into `b`:b.unshift.apply( b, a );b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]

That’s a lot nicer, right!? Especially since the unshift(..) approach here doesn’t need to worry about the reverse ordering as in the previous attempts. ES6’s spread operator will be even nicer: a.push( ...b ) or b.unshift( ...a ).

But, things aren’t as rosy as they might seem. In both cases, passing either a or b to apply(..)‘s second argument (or via the ... spread operator) means that the array is being spread out as arguments to the function.

The first major problem is that we’re effectively doubling the size (temporarily, of course!) of the thing being appended by essentially copying its contents to the stack for the function call. Moreover, different JS engines have different implementation-dependent limitations to the number of arguments that can be passed.

So, if the array being added on has a million items in it, you’d almost certainly way exceed the size of the size of the stack allowed for that push(..) or unshift(..) call. Ugh. It’ll work just fine for a few thousand elements, but you have to be careful not to exceed a reasonably safe limit.

Note: You can try the same thing with splice(..), but you’ll have the same conclusions as with push(..) / unshift(..).

One option would be to use this approach, but batch up segments at the max safe size:

function combineInto(a,b) {    var len = a.length;    for (var i=0; i  len; i=i+5000) {        b.unshift.apply( b, a.slice( i, i+5000 ) );    }}

Wait, we’re going backwards in terms of readability (and perhaps even performance!). Let’s quit before we give up all our gains so far.

Summary

Array#concat(..)es el enfoque probado y verdadero para combinar dos (¡o más!) matrices. Pero el peligro oculto es que está creando una nueva matriz en lugar de modificar una de las existentes.

Hay opciones que se modifican in situ, pero tienen varias compensaciones.

Teniendo en cuenta las diversas ventajas y desventajas, quizás la mejor de todas las opciones (incluidas otras que no se muestran) sea reduce(..)y reduceRight(..).

Cualquiera que sea su elección, probablemente sea una buena idea pensar críticamente sobre su estrategia de fusión de matrices en lugar de darla por sentado.

Acerca de Kyle Simpson

Kyle Simpson es un ingeniero de software orientado a la web, ampliamente aclamado por su serie de libros “You Don't Know JS” y por casi 1 millón de horas de vistas de sus cursos en línea. El superpoder de Kyle es hacer mejores preguntas, quien cree profundamente en utilizar al máximo las herramientas mínimamente necesarias para cualquier tarea. Como “tecnólogo centrado en lo humano”, le apasiona unir a los humanos y la tecnología, haciendo evolucionar las organizaciones de ingeniería para resolver los problemas correctos, de maneras más simples. Kyle siempre luchará por las personas detrás de los píxeles.

Publicaciones de github.com

Te podría interesar...

Deja una respuesta

Subir