💾 Archived View for undevlambda.space › 2023-06-22-la-fonction-map.gmi captured on 2024-08-18 at 17:14:20. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-12-28)
-=-=-=-=-=-=-
La fonction "map" est très utile lorsqu'on souhaite changer chaque élément d'une collection de données, à l'aide d'une fonction. Je me souviens, lorsque j'ai découvert son existence et son utilisation, avoir eu du mal à comprendre son fonctionnement. Je vous propose de la redécouvrir, et de la construire, à l'aide de quelques exemples de code dans un style impératif. J'utiliserai javascript comme langage de programmation.
Voici un exercice un peu bateau mais néanmoins utile. L'exercice consiste à transformer une liste de nombres en liste de nombres sous forme de chaîne de caractères.
function exemple1() { const nombres = [1, 2, 3, 4]; const resultat = []; for(let i = 0; i < nombres.length; i++) { resultat.push("" + nombres[i]); } return resultat; } function main() { console.log("résultat exemple 1: ", exemple1()); }
Nous utilisons une boucle "for", et à chaque itération, nous transformons chaque en nombre en chaîne de caractères.
Imaginons que votre client souhaite faire une campagne de publicité par SMS en envoyant un SMS à tous ces clients. Un client est représenté par l'objet littéral suivant :
const client = { nom: "John Doe", adresseCourriel: "john@doe.com", telephone: "0601020304" };
Il nous faudrait extraire tous les numéros de téléphone. Voici la solution avec une boucle "for" :
function exemple2() { const clients = [ {nom: "John Doe", adresseCourriel: "john@doe.com", telephone: "0601020304"}, {nom: "Jane Doe", adresseCourriel: "jane@doe.com", telephone: "0604030201"}, {nom: "Homer Simpson", adresseCourriel: "homer@simpson.com", telephone: null} ]; const resultat = []; for(let i = 0; i < clients.length; i++) { resultat.push(clients[i].telephone); } return resultat; } function main() { console.log("résultat exemple 2: ", exemple2()); }
Si nous comparons les 2 solutions proposées, bien qu'elles répondent à un besoin différent, nous pouvons remarquer qu'elles se ressemblent, font le même type de travail, à savoir transformer les éléments d'une collection de donnée. Est-il possible d'extraire une fonction qui permette d'appliquer une fonction sur chacun des éléments d'une collection en entrée, pour en obtenir une collection de données de sortie ?
Essayons de faire apparaître cette fonction avec une série de petits refactoring. Commençons d'abord par passer en paramètre la collection de données.
function exemple1(nombres) { const resultat = []; for(let i = 0; i < nombres.length; i++) { resultat.push("" + nombres[i]); } return resultat; } function main() { console.log("résultat exemple1: ", exemple1([1, 2, 3, 4]); }
function exemple2(clients) { const resultat = []; for(let i = 0; i < clients.length; i++) { resultat.push(clients[i].telephone); } return resultat; } function main() { const clients = [ {nom: "John Doe", adresseCourriel: "john@doe.com", telephone: "0601020304"}, {nom: "Jane Doe", adresseCourriel: "jane@doe.com", telephone: "0604030201"}, {nom: "Homer Simpson", adresseCourriel: "homer@simpson.com", telephone: null} ]; console.log("résultat exemple 2: ", exemple2(clients)); }
Nous voyons que "exemple1()" et "exemple2()" se ressemblent de plus en plus. Essayons maintenant d'extraire une fonction, appliqué à chaque élément, et dont le résultat est stocké dans la collection de sortie.
function exemple1(nombres) { function nombreToString(n) { return "" + n; } const resultat = []; for(let i = 0; i < nombres.length; i++) { resultat.push(nombreToString(nombres[i])); } return resultat; } function main() { console.log("résultat exemple1: ", exemple1([1, 2, 3, 4]); }
function exemple2(clients) { function extrairePhone(client) { return client.telephone; } const resultat = []; for(let i = 0; i < clients.length; i++) { resultat.push(extrairePhone(clients[i]); } return resultat; } function main() { const clients = [ {nom: "John Doe", adresseCourriel: "john@doe.com", telephone: "0601020304"}, {nom: "Jane Doe", adresseCourriel: "jane@doe.com", telephone: "0604030201"}, {nom: "Homer Simpson", adresseCourriel: "homer@simpson.com", telephone: null} ]; console.log("résultat exemple2: ", exemple2(clients)); }
Si nous observons la boucle "for" des 2 exemples, nous voyons qu'elle fournit le même type de travail. Faisons maintenant en sorte que les fonctions "nombreToString" et "extrairePhone" soient fournies en paramètre.
function exemple1(nombres, fn) { const resultat = []; for(let i = 0; i < nombres.length; i++) { resultat.push(fn(nombres[i])); } return resultat; } function main() { function nombreToString(n) { return "" + n; } console.log("résultat exemple1: ", exemple1([1, 2, 3, 4], nombreToString)); }
function exemple2(clients, fn) { const resultat = []; for(let i = 0; i < clients.length; i++) { resultat.push(fn(clients[i])); } return resultat; } function main() { function extrairePhone(client) { return client.telephone; } const clients = [ {nom: "John Doe", adresseCourriel: "john@doe.com", telephone: "0601020304"}, {nom: "Jane Doe", adresseCourriel: "jane@doe.com", telephone: "0604030201"}, {nom: "Homer Simpson", adresseCourriel: "homer@simpson.com", telephone: null} ]; console.log("résultat exemple 2: ", exemple2(clients, extrairePhone)); }
Il est maintenant clair que "exemple1()" et "exemple2()" font le même travail. Nous venons d'écrire la fonction map ! Voici la fonction map révélée, avec un dernier refactoring.
function map(collection, fn) { const resultat = []; for(let i = 0; i < collection.length; i++) { resultat.push(fn(collection[i])); } return resultat; } function main() { // exemple 1 const nombres = [1,2,3,4]; function nombreToString(nombre) { return "" + nombre; } console.log("résultat exemple1: ", map(nombres, nombreToString)); // exemple 2 const clients = [ {nom: "John Doe", adresseCourriel: "john@doe.com", telephone: "0601020304"}, {nom: "Jane Doe", adresseCourriel: "jane@doe.com", telephone: "0604030201"}, {nom: "Homer Simpson", adresseCourriel: "homer@simpson.com", telephone: null} ]; function extrairePhone(client) { return client.telephone; } console.log("résultat exemple2: ", map(clients, extrairePhone)); }
Nous avons maintenant dans notre arsenal la fonction "map".
Nous venons de voir que la fonction "map" nous a permis d'abstraire le principe de parcours d'une collection de données pour y appliquer une fonction de transformation sur chaque élément. J'aimerai ajouter que son nom devra vous évoquer par la suite ce principe, quelque soit le langage dans lequel vous la verrez utilisée. L'intention du code devient ainsi plus limpide, plus immédiat.
Notons également que la fonction map n'a pas muté la collection de données d'entrée, elle en a construit une nouvelle.
Nous verrons prochainement les fonctions "filter()" et "reduce()", qui sont également des abstractions autour des collections de données.