Foto de um Panda de Óculos

@expalmer

A Javascript Enthusiast

Juntos Aprendemos Mais :)

Criando seu próprio Array MergeAll e Zip em Javascript

javascript

Neste post criaremos duas funções auxiliares chamadas de mergeAll e zip. Com elas teremos mais opções ao usar funções encadeadas junto com map, filter e reduce.

Para que serve o mergeAll ?

Essa função serve para juntar um array multidimencional, ou seja, um array que contém outro array em cada um de seus itens. Por exemplo se temos um array assim [ [1,2,3], [4,5,6], [7,8,9] ], e para que ele fique assim [1,2,3,4,5,6,7,8,9], precisamos juntá-los.

mergeAll

Array.prototype.mergeAll = function() {
    var results = [];
    this.forEach(function(subArray) {
        subArray.forEach(function( x ) {
            results.push( x );
        });
    });
    return results;
};

Aqui pegamos os resultados usando nosso anteriormente criado forEach, e iteramos cada um dos itens cancatenando-os, e no final temos um único array.

Pegando apenas os nomes

Vamos usar esses dados de exemplo.

var lordOfTheRings = [
  {
    race: 'Elves',
    characters: [
      {
        name: "Galadriel",
        weapons: [ "Elven Magic", "Nenya" ]
      },
      {
        name: "Legolas",
        weapons: [ "Bow", "Knife" ]
      }
    ]
  },
  {
    race: 'Maiar',
    characters: [
      {
        name: "Gandalf",
        weapons: [ "Glamdring", "Wizard Staff", "Sword" ]
      },
      {
        name: "Radagast",
        weapons: [ "Powers of the Maiar", "Wizard Staff" ]
      }
    ]
  },
  {
    race: 'Men',
    characters: [
      {
        name: "Aragorn",
        weapons: [ "Anduril", "Sword" ]
      },
      {
        name: "Faramir",
        weapons: [ "Bow", "Sword" ]
      }
    ]
  },
  {
    race: "Ainur",
    characters: [
      {
        name: "Sauron",
        weapons: [ "One Ring", "Sword", "Powers of the Maiar" ]
      }
    ]
  }
];

O que preciso é criar um array com todos os nomes de cada um dos personagens que estão separados por raça.

Vamos primeiramente fazer assim:

var names = lordOfTheRings
  .map(function(item){
    return item.characters
      .map(function( character ) {
        return character.name;
      });
  });

console.log(names);
/*
[ 
  [ 'Galadriel', 'Legolas' ],
  [ 'Gandalf', 'Radagast' ],
  [ 'Aragorn', 'Faramir' ],
  [ 'Sauron' ] 
]
*/

Note que temos 4 arrays dentro de um array... então usamos o mergeAll.

var names = lordOfTheRings
  .map(function(item){
    return item.characters
      .map(function( character ) {
        return character.name;
      });
  }).mergeAll();

console.log(names);
/*
[ 'Galadriel',
  'Legolas',
  'Gandalf',
  'Radagast',
  'Aragorn',
  'Faramir',
  'Sauron' ]
*/

No final de nosso map, chamamos a função mergeAll, e agora sim temos um único array :)

Para que serve o zip ?

Essa função serve para iterar dois arrays ao mesmo tempo, aplicando uma função callback para ambos. Isso é útil quando temos dois arrays que possuem alguma ligação em relação aos seus indíces.

zip

Array.zip = function(left, right, callback) {
  var len = Math.min(left.length, right.length); // pegamos o menor tamanho
  var results = [];
  for( var i = 0; i < len; i++ ) {
    results.push( callback(left[i], right[i]) );
  }
  return results;
};

O que fazemos aqui é receber 3 argumentos, (1) o primeiro array, (2) o segundo array e (3) a função callback que vai iterar um por um dos itens dos array.

Note que se os arrays forem de tamanhos diferentes, ele irá pegar o tamanho do array menor. E também que essa função não vai no prototype, pois passamos duas referências de objetos/arrays diferentes.

Ligando dois arrays

Imagine esse cenário, onde no primeiro array, tenho os personagens e no segundo tenho as armas. O que eu preciso, é um novo objeto que pegue do primeito array apenas o name, e pegue do segundo array apenas a último item de weapons.

var characters = [
  { name: "Galadriel" },
  { name: "Legolas" },
  { name: "Gandalf" },
  { name: "Radagast" },
  { name: "Aragorn" },
  { name: "Faramir" },
  { name: "Sauron" }
];

var weapons = [
  [ "Elven Magic", "Nenya" ],
  [ "Bow", "Knife" ],
  [ "Glamdring", "Wizard Staff", "Sword" ],
  [ "Powers of the Maiar", "Wizard Staff" ],
  [ "Anduril", "Sword" ],
  [ "Bow", "Sword" ],
  [ "One Ring", "Sword", "Powers of the Maiar" ]
];

var joined = Array.zip( characters, weapons, function( character, weapons ) {
  return { name: character.name, weapon: weapons.pop() };
});

console.log(joined);

/*
[ 
  { name: 'Galadriel', weapon: 'Nenya' },
  { name: 'Legolas', weapon: 'Knife' },
  { name: 'Gandalf', weapon: 'Sword' },
  { name: 'Radagast', weapon: 'Wizard Staff' },
  { name: 'Aragorn', weapon: 'Sword' },
  { name: 'Faramir', weapon: 'Sword' },
  { name: 'Sauron', weapon: 'Powers of the Maiar' } 
]
*/

Pronto, agora temos um único objeto.

Agora que temos todas as funções que precisamos, map, filter, reduce, mergeAll e zip, nos próximos posts veremos como usá-las juntas para criarmos consultas interessantes e super úteis.

Espero que tenham gostado. That's it !