Criando seu próprio Array Foreach, Array Map e Array Filter em Javascript
Vou postar uma série de Criando seu próprio alguma coisa em javascript para deixar registrado meus estudos e espero que sirva de ajuda para você.
Estudando Programação Funcional em Javascript
Javascript é uma linguagem considerada Híbrida, pois é possível aplicar paradigmas de programação tais como Imperativa, Declarativa e Funcional.
No caso tenho estudado Programação Funcional e com isso aprendi que dominando funções como map
, filter
, reduce
, mergeAll
e zip
, fará você ter super poderes.
Nas palavras de Jafar Husain da NetFlix: "Se você aprender essas 5 funções, seu código vai se tornar menor, mais auto descritivo e mais durável".
Vamos começar a aprender esses truques. Neste posts vamos ver map e filter, mas vou incluir o forEach antes de tudo:
Considere esse array de objetos
var lordOfTheRings = [
{ name: "Galadriel", race: "Elves", weapons: [ "Elven Magic", "Nenya" ] },
{ name: "Legolas", race: "Elves", weapons: [ "Bow", "Knife" ] },
{ name: "Gandalf", race: "Maiar", weapons: [ "Glamdring", "Wizard Staff", "Sword" ] },
{ name: "Radagast", race: "Maiar", weapons: [ "Powers of the Maiar", "Wizard Staff" ] },
{ name: "Aragorn", race: "Men", weapons: [ "Anduril", "Sword" ] },
{ name: "Sauron", race: "Ainur", weapons: [ "One Ring", "Sword", "Powers of the Maiar" ] },
{ name: "Faramir", race: "Men", weapons: [ "Bow", "Sword" ] }
];
Você precisa mostrar apenas o name e a race de cada personagem, então vamos usar um for
.
var results = [];
var len = lordOfTheRings.length;
for (var i = 0; i < len; i++) {
results.push( { name: lordOfTheRings[i].name, race: lordOfTheRings[i].race } );
}
console.log( results );
/*
[ { name: 'Galadriel', race: 'Elves' },
{ name: 'Legolas', race: 'Elves' },
{ name: 'Gandalf', race: 'Maiar' },
{ name: 'Radagast', race: 'Maiar' },
{ name: 'Aragorn', race: 'Men' },
{ name: 'Sauron', race: 'Ainur' },
{ name: 'Faramir', race: 'Men' } ]
*/
Vamos implementar nosso próprio forEach
.
forEach
// https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
// Para cada item do array é chamado o callback com 3 argumentos
// 1: O this[ i ], 2: o index atual, 3: o array inteiro
Array.prototype.forEach = function ( callback ) {
var len = this.length; // tamanho do nosso array
for( var i = 0; i < len; i++ ) {
callback( this[i], i, this );
}
};
OBS: Falar sobre prototype
é coisa para outro post, embora tenham muitos outros posts pela internet explicando. Mas vamos a parte que nos interessa. Quando colocamos no prototype do objeto Array a função forEach, isso fará com que todos arrays tenham acesso a essa função. Note que o this é o próprio array.
Vamos usar nossa implementação.
var results = [];
lordOfTheRings.forEach( function( item ) {
results.push( { name: item.name, race: item.race } );
});
console.log( results );
/*
[ { name: 'Galadriel', race: 'Elves' },
{ name: 'Legolas', race: 'Elves' },
{ name: 'Gandalf', race: 'Maiar' },
{ name: 'Radagast', race: 'Maiar' },
{ name: 'Aragorn', race: 'Men' },
{ name: 'Sauron', race: 'Ainur' },
{ name: 'Faramir', race: 'Men' } ]
*/
// Só para você testar usando as chaves ;)
lordOfTheRings.forEach( function( item, index ) {
console.log( index, item.name );
});
/*
0 '=>' 'Galadriel'
1 '=>' 'Legolas'
2 '=>' 'Gandalf'
3 '=>' 'Radagast'
4 '=>' 'Aragorn'
5 '=>' 'Sauron'
6 '=>' 'Faramir'
*/
Olha aí cara!! Você mesmo implementou seu próprio forEach, vamos adiante!
Imagine que agora você precise de um novo array de objetos apenas contendo name e weapons. Com forEach faríamos assim:
var result = [];
lordOfTheRings.forEach( function( item ) {
result.push( { name: item.name, weapons: item.weapons } );
});
console.log(result);
/*
[ { name: 'Galadriel', weapons: [ 'Elven Magic', 'Nenya' ] },
{ name: 'Legolas', weapons: [ 'Bow', 'Knife' ] },
{ name: 'Gandalf', weapons: [ 'Glamdring', 'Wizard Staff', 'Sword' ] },
{ name: 'Radagast', weapons: [ 'Powers of the Maiar', 'Wizard Staff' ] },
{ name: 'Aragorn', weapons: [ 'Anduril', 'Sword' ] },
{ name: 'Sauron', weapons: [ 'One Ring', 'Sword', 'Powers of the Maiar' ] },
{ name: 'Faramir', weapons: [ 'Bow', 'Sword' ] } ]
*/
map
Agora sim, vamos criar nosso array map, que irá retornar um novo array conforme nossa necessidade. Essa função é extremamente útil, pois com ela futuramente você verá como podemos encadear funções.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
// Mesmo coisa que o forEach, mas guardamos os dados para retornar após o "for"
Array.prototype.map = function( callback ) {
var results = [];
var len = this.length;
for( var i = 0; i < len; i++ ) {
results.push( callback( this[i], i, this ) );
}
return results; // <= retornamos aqui
}
Agora usando nosso map.
var result = lordOfTheRings.map( function ( item ) {
return { name: item.name, weapons: item.weapons };
});
console.log( result );
/*
[ { name: 'Galadriel', weapons: [ 'Elven Magic', 'Nenya' ] },
{ name: 'Legolas', weapons: [ 'Bow', 'Knife' ] },
{ name: 'Gandalf', weapons: [ 'Glamdring', 'Wizard Staff', 'Sword' ] },
{ name: 'Radagast', weapons: [ 'Powers of the Maiar', 'Wizard Staff' ] },
{ name: 'Aragorn', weapons: [ 'Anduril', 'Sword' ] },
{ name: 'Sauron', weapons: [ 'One Ring', 'Sword', 'Powers of the Maiar' ] },
{ name: 'Faramir', weapons: [ 'Bow', 'Sword' ] } ]
*/
Até aqui tudo bem ? Viu que o map retorna um novo array! Seguindo em frente vamos para exemplos mais interessantes.
Agora queremos listar apenas os Elfos! Para isso teremos que usar um forEach, fazer um if e guardar os resultados que queremos:
var elfos = [];
lordOfTheRings.forEach( function ( item ) {
if( item.race === "Elves" ) {
elfos.push( { name: item.name, race: item.race } );
}
});
console.log( elfos );
/*
[ { name: 'Galadriel', race: 'Elves' },
{ name: 'Legolas', race: 'Elves' } ]
*/
Vamos criar nossa função filter, ela será responsável de filtrar os dados que desejamos. O que ela faz é percorrer cada item do array e se o retorno for verdadeiro o item é acumulado.
filter
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Array.prototype.filter = function ( callback ) {
var results = [];
var len = this.length;
for( var i = 0; i < len; i++ ) {
if ( callback( this[i] ) ) { // se for verdadeiro é acumulado
results.push( this[i] );
}
}
return results;
};
Aqui já entramos nas utilidades de encadear funções, vamos refazer nosso exemplo com filter e map.
var elfos = lordOfTheRings
.filter( function ( item ) {
return item.race === "Elves";
})
.map( function( elfos ) {
return { name: elfos.name, race: elfos.race };
});
console.log( elfos );
/*
[ { name: 'Galadriel', race: 'Elves' },
{ name: 'Legolas', race: 'Elves' } ]
*/
Vamos fazer mais uns testezinhos com map e filter para fixar bem na mente.
var numbers = [ 1,2,3,4,5,6,7,8,9 ];
// mostrandos só os numeros pares
var even = numbers.filter( function( n ) {
return n % 2 === 0; //
});
console.log(even);
/*
[ 2, 4, 6, 8 ]
*/
// pegue os numeros impares e multiplique por 10
var odd = numbers
.filter( function( n ) {
return n % 2; //
})
.map( function( n ) {
return n * 100;
})
console.log(odd);
/*
[ 100, 300, 500, 700, 900 ]
*/
Até aqui já dá para perceber a variedade de coisas que podemos fazer apenas com map
e filter
. E o melhor de tudo é que você mesmo criou cada uma dessas funções :).
Finalizando
Para finalizar, vamos fazer mais um exemplo, e logo abaixo coloco 2 desafios para você.
// Só quero quem é Maiar ou Men, e a quantidade total de weapons de cada um.
// Retorne um array de objetos somente com as chaves {name},{totalWeapons}
var result = lordOfTheRings
.filter( function( item ) {
return item.race === "Maiar" || item.race === "Men";
})
.map( function( item ) {
return { name: item.name, totalWeapons: item.weapons.length };
});
console.log(result);
/*
[ { name: 'Gandalf', totalWeapons: 3 },
{ name: 'Radagast', totalWeapons: 2 },
{ name: 'Aragorn', totalWeapons: 2 },
{ name: 'Faramir', totalWeapons: 2 } ]
*/
Desafio 1
Me retorne todos ( menos o Sauron ) que possuam a quantidade total de armas maior que 2.
O array de objetos deve ser neste formato:
/*
[ { name: 'Name', totalWeapons: 0 } ]
*/
Desafio 2
Me retorne somente aqueles que possuam mais de 2 armas e entre elas deve conter uma "Sword".
Só use map
e filter
, não pode usar if
.
O array de objetos deve ser neste formato:
/*
[ { name: 'Name', race: "Race", totalWeapons: 0 } ]
*/
Poste nos comentários as soluções dos desafios.
No próximo post vamos implementar o reduce.
Espero que tenham gostado. That's it !