La lliçó anterior va acabar amb una observació incòmoda: gairebé totes les funcions que vas escriure recorrien la llista a mà amb un bucle, i no sempre amb el mateix. És normal, perquè JavaScript ofereix sis maneres diferents de recórrer un array i cap no és la millor en tots els casos. Triar bé no és una qüestió estètica: hi ha formes que et donen l'índex i d'altres que no, formes que es poden interrompre a mig camí i d'altres que no, formes que retornen un resultat i formes que només produeixen efectes. En aquesta lliçó les veuràs totes sis amb les seves regles exactes, construiràs la taula comparativa que resol l'elecció en cinc segons, i les aplicaràs a dues feines reals de Nómada Tasques: pintar el tauler de Taller Nómada per consola i transformar el backlog en línies d'informe per a la Marta.

Contingut

  1. Les sis formes, d'un cop d'ull
  2. El for clàssic: control total
  3. for...of: l'opció per defecte
  4. for...in sobre arrays: l'error freqüent
  5. forEach: recórrer amb una funció
  6. Per què forEach no es pot tallar
  7. entries(), keys() i values()
  8. map: recórrer per transformar
  9. La taula comparativa
  10. Recórrer arrays d'objectes i arrays imbricats
  11. L'error de mutar l'array mentre es recorre
  12. Exemple integrat: pintar el tauler de Taller Nómada
  13. Errors Habituals i Consells
  14. Exercicis
  15. Conclusió

  1. Les sis formes, d'un cop d'ull

Abans del detall, aquí les tens totes sis recorrent el mateix array:

const responsables = ['Iván', 'Marta', 'Lucía'];

// 1. for clàssic
for (let i = 0; i < responsables.length; i++) console.log(responsables[i]);

// 2. for...of
for (const persona of responsables) console.log(persona);

// 3. for...in  (sobre arrays gairebé sempre és un error!)
for (const index in responsables) console.log(responsables[index]);

// 4. forEach
responsables.forEach((persona) => console.log(persona));

// 5. entries() per tenir índex i valor
for (const parell of responsables.entries()) console.log(parell[0], parell[1]);

// 6. map (recorre transformant)
const enMajuscules = responsables.map((persona) => persona.toUpperCase());

Totes sis imprimeixen (o produeixen) el que s'espera amb aquest array. Les diferències apareixen tan bon punt necessites l'índex, vols sortir abans d'hora, l'array té forats o dins del bucle hi ha operacions asíncrones.

flowchart TD
    A["Vull recórrer un array"] --> B{"Necessito<br/>transformar-lo?"}
    B -->|Sí| C["map"]
    B -->|No| D{"Necessito<br/>interrompre?"}
    D -->|Sí| E["for...of<br/>(o for clàssic)"]
    D -->|No| F{"Necessito<br/>l'índex?"}
    F -->|Sí| G["for...of + entries()"]
    F -->|No| H["for...of o forEach"]

  1. El for clàssic: control total

El coneixes des de 02-02. La seva virtut és que tu controles el comptador, així que pots fer coses que cap altra forma no permet.

const tasques = ['T1', 'T2', 'T3', 'T4', 'T5', 'T6'];

// Recorregut normal
for (let i = 0; i < tasques.length; i++) {
  console.log(`${i + 1}. ${tasques[i]}`);
}

// Cap enrere
for (let i = tasques.length - 1; i >= 0; i--) {
  console.log(tasques[i]);       // T6, T5, T4...
}

// De dos en dos
for (let i = 0; i < tasques.length; i += 2) {
  console.log(tasques[i]);       // T1, T3, T5
}

// Comparar cada element amb el següent
for (let i = 0; i < tasques.length - 1; i++) {
  console.log(`${tasques[i]} va abans que ${tasques[i + 1]}`);
}

Aquests quatre casos —cap enrere, amb salt, aturant-se abans del final, mirant el veí— són la raó per la qual el for clàssic continua viu. Fora d'ells, resulta sorollós: cal escriure tres expressions, declarar una variable que només serveix de comptador i accedir amb tasques[i] en lloc de treballar amb l'element directament. I té un risc propi: el desbordament per un (off-by-one), és a dir, escriure <= en lloc de < i llegir undefined a l'última volta.

for (let i = 0; i <= tasques.length; i++) {   // ✗ una volta de més
  console.log(tasques[i]);                    // l'última imprimeix undefined
}

  1. for...of: l'opció per defecte

for...of recorre els valors de l'array, sense comptador i sense índexs.

const backlog = [
  { id: 1, titol: 'Redissenyar la sala polivalent',           responsable: 'Iván',  horesEstimades: 12, estat: 'en-curs' },
  { id: 2, titol: 'Cartelleria del taller de serigrafia',     responsable: 'Marta', horesEstimades: 6,  estat: 'pendent' },
  { id: 3, titol: 'Actualitzar el web de reserves',           responsable: 'Lucía', horesEstimades: 14, estat: 'pendent' },
  { id: 4, titol: 'Inventari de tintes de serigrafia',        responsable: 'Marta', horesEstimades: 3,  estat: 'feta' },
  { id: 5, titol: 'Guia sobre enquadernació per a residents', responsable: 'Iván',  horesEstimades: 8,  estat: 'en-curs' },
  { id: 6, titol: 'Pressupost de la fusteria',                responsable: 'Iván',  horesEstimades: 5,  estat: 'pendent' }
];

for (const tasca of backlog) {
  console.log(`${tasca.titol} — ${tasca.responsable}`);
}

Quatre motius pels quals és l'opció per defecte:

  1. No hi ha índexs que puguis equivocar. Desapareixen la i, el <, el ++ i el risc d'off-by-one.
  2. Es pot interrompre amb break, continue i return, igual que un for clàssic.
  3. const funciona de debò. Cada volta crea una variable nova, així que la pots declarar const (una cosa impossible al for clàssic, on el comptador canvia).
  4. Funciona amb qualsevol iterable, no només amb arrays: strings, Map, Set, NodeList del DOM (Mòdul 6)…
// break i continue funcionen amb normalitat
for (const tasca of backlog) {
  if (tasca.estat === 'feta') continue;          // salta les acabades
  if (tasca.horesEstimades > 12) {
    console.log(`Massa gran: ${tasca.titol}`);
    break;                                        // surt del bucle
  }
}

L'única mancança que té és que no dóna l'índex. Per a això hi ha entries() (apartat 7).

  1. for...in sobre arrays: l'error freqüent

for...in està pensat per a objectes, com vas veure a 04-01: recorre les claus. Sobre un array recorre els índexs, però com a strings, i això genera dos problemes.

const hores = [12, 6, 14];

for (const i in hores) {
  console.log(typeof i, i, hores[i]);
}
// string 0 12
// string 1 6
// string 2 14

Problema 1: els índexs són strings. El + amb strings concatena, així que qualsevol aritmètica sobre l'índex falla en silenci:

for (const i in hores) {
  console.log(`Tasca ${i + 1}`);     // ✗ 'Tasca 01', 'Tasca 11', 'Tasca 21'
}

Problema 2: recorre totes les propietats enumerables, no només els índexs. Incloses les que algú hagi afegit a l'array o al prototip:

const etiquetes = ['espai', 'disseny'];
etiquetes.autor = 'Lucía';

for (const clau in etiquetes) console.log(clau);
// 0
// 1
// autor      ← ✗ no és un element

for (const valor of etiquetes) console.log(valor);
// espai
// disseny    ← ✓ només elements

Hi ha un tercer detall: for...in salta els forats d'un array dispers, mentre que for...of els recorre retornant undefined. Sumat a tot l'anterior, la conclusió és taxativa:

for...in per a objectes; for...of per a arrays. Si veus un for...in sobre un array en codi d'altri, gairebé sempre és un error o codi anterior al 2015.

  1. forEach: recórrer amb una funció

forEach és el primer dels mètodes d'ordre superior que vas estudiar a 03-06, i ara el fas servir en la seva versió nativa. Rep un callback al qual crida una vegada per element, amb tres arguments: valor, índex i l'array complet.

backlog.forEach((tasca, index, array) => {
  console.log(`${index + 1} de ${array.length}: ${tasca.titol}`);
});
// 1 de 6: Redissenyar la sala polivalent
// 2 de 6: Cartelleria del taller de serigrafia
// ...

A la pràctica el tercer argument gairebé mai no es fa servir, i moltes vegades tampoc el segon:

backlog.forEach((tasca) => console.log(tasca.titol));

Les seves característiques, en positiu i en negatiu:

  • Retorna undefined. No produeix res; només serveix per a efectes (imprimir, desar, modificar alguna cosa de fora).
  • Dóna l'índex sense esforç, a diferència de for...of.
  • Salta els forats d'un array dispers.
  • No es pot interrompre: és el tema de l'apartat següent.
  • Cada element es processa en una crida a funció, amb el seu propi àmbit, cosa que evita del tot el problema de la variable compartida que vas estudiar a 03-04 amb var.

Un ús típic al projecte: notificar els responsables de les tasques vençudes.

const AVUI = '2026-09-20';

backlog.forEach((tasca) => {
  if (tasca.dataLimit < AVUI && tasca.estat !== 'feta') {
    console.log(`⚠ ${tasca.responsable}: «${tasca.titol}» va vèncer el ${tasca.dataLimit}`);
  }
});

  1. Per què forEach no es pot tallar

Aquest és el punt que més confon. Un break dins d'un forEach és directament un error de sintaxi:

backlog.forEach((tasca) => {
  if (tasca.id === 3) break;      // ✗ SyntaxError: Illegal break statement
});

La raó és estructural. break serveix per sortir d'un bucle, i dins de forEach no ets en un bucle: ets dins del cos d'una funció que forEach invoca una vegada i una altra. Recorda com vas implementar elMeuForEach a 03-06:

function elMeuForEach(array, callback) {
  for (let i = 0; i < array.length; i++) {
    callback(array[i], i, array);       // ← el bucle és AQUÍ, fora de la teva funció
  }
}

El teu callback no té cap poder sobre aquell for. I return dins del callback tampoc no talla res: simplement acaba aquella crida i forEach passa a l'element següent. Funciona com un continue, no com un break:

backlog.forEach((tasca) => {
  if (tasca.estat === 'feta') return;    // ← actua com un continue
  console.log(tasca.titol);
});

Què fer quan necessites aturar-te de debò?

Situació Solució
Necessites aturar-te tan bon punt trobis alguna cosa for...of amb break
Cerques un element find / findIndex (04-05)
Només vols saber si algun compleix some (04-05)
Vols saber si tots compleixen every (04-05)
Vols quedar-te amb uns quants filter (04-05)

I un antipatró que veuràs i no has de copiar: llançar una excepció per forçar la sortida.

// ✗ No facis mai això: fer servir el sistema d'errors com a control de flux
try {
  backlog.forEach((t) => { if (t.id === 3) throw new Error('trobat'); });
} catch (e) { }

És lent, amaga errors reals i confon qui llegeixi el codi. Si necessites break, fes servir for...of.

  1. entries(), keys() i values()

Aquests tres mètodes retornen iteradors (una estructura que produeix valors sota demanda; els veuràs a fons a 05-08), pensats per fer-se servir amb for...of.

const responsables = ['Iván', 'Marta', 'Lucía'];

// keys(): els índexs
for (const i of responsables.keys()) console.log(i);          // 0, 1, 2

// values(): els valors (equival a for...of directe)
for (const v of responsables.values()) console.log(v);        // Iván, Marta, Lucía

// entries(): parells [índex, valor]
for (const parell of responsables.entries()) {
  console.log(`${parell[0]} → ${parell[1]}`);
}
// 0 → Iván
// 1 → Marta
// 2 → Lucía

entries() resol exactament la mancança de for...of: et dóna índex i valor, conservant la possibilitat de fer servir break. Amb desestructuració —que estudiaràs a 04-06— s'escriu de la manera com realment es fa servir:

for (const [index, tasca] of backlog.entries()) {
  console.log(`${index + 1}. ${tasca.titol}`);
  if (index === 2) break;             // ← aquí break SÍ que funciona
}
// 1. Redissenyar la sala polivalent
// 2. Cartelleria del taller de serigrafia
// 3. Actualitzar el web de reserves

Compara les tres alternatives quan necessites índex i valor:

// A. for clàssic: funciona, però és sorollós
for (let i = 0; i < backlog.length; i++) console.log(i, backlog[i].titol);

// B. forEach: llegible, però no es pot tallar
backlog.forEach((t, i) => console.log(i, t.titol));

// C. for...of + entries(): llegible I es pot tallar   ← la millor de les tres
for (const [i, t] of backlog.entries()) console.log(i, t.titol);

  1. map: recórrer per transformar

map es mereix un apartat a part perquè canvia el propòsit del recorregut. forEach i for...of recorren per fer alguna cosa; map recorre per produir un array nou amb un element per cada element original.

El vas implementar a mà a 03-06 com a elMeuMap; el natiu funciona igual:

const titols = backlog.map((tasca) => tasca.titol);
console.log(titols.length);      // 6
console.log(titols[0]);          // 'Redissenyar la sala polivalent'

const enMinuts = backlog.map((tasca) => tasca.horesEstimades * 60);
console.log(enMinuts);           // [ 720, 360, 840, 180, 480, 300 ]

console.log(backlog.length);     // 6  ← l'original, intacte

Les seves tres propietats definidores, que convé gravar:

  1. L'array resultant té sempre la mateixa longitud que l'original. map no filtra ni descarta: si n'entren sis, en surten sis. Per quedar-te amb menys, filter (04-05).
  2. No muta l'original. En retorna un de nou.
  3. El callback ha de retornar alguna cosa. Oblidar el return és l'error número u amb map.
// ✗ L'error clàssic: claus sense return
const malament = backlog.map((tasca) => { tasca.titol; });
console.log(malament);   // [ undefined, undefined, undefined, undefined, undefined, undefined ]

// ✓ Amb return explícit
const correcteA = backlog.map((tasca) => { return tasca.titol; });

// ✓ O amb retorn implícit, sense claus
const correcteB = backlog.map((tasca) => tasca.titol);

Aquesta distinció ve directament de 03-02: una fletxa sense claus retorna l'expressió; una fletxa amb claus necessita return explícit.

Aplicació al projecte: convertir el backlog en línies d'informe. Compara la mateixa feina amb forEach i amb map:

const MARQUES = { feta: '✓', 'en-curs': '▸', pendent: '○' };

// Amb forEach: cal crear un array auxiliar i empènyer
const liniesA = [];
backlog.forEach((tasca) => {
  liniesA.push(`${MARQUES[tasca.estat]} [${tasca.id}] ${tasca.titol}`);
});

// Amb map: l'array és el resultat, sense variables auxiliars
const liniesB = backlog.map((tasca) => `${MARQUES[tasca.estat]} [${tasca.id}] ${tasca.titol}`);

console.log(liniesB.join('\n'));
// ▸ [1] Redissenyar la sala polivalent
// ○ [2] Cartelleria del taller de serigrafia
// ○ [3] Actualitzar el web de reserves
// ✓ [4] Inventari de tintes de serigrafia
// ▸ [5] Guia sobre enquadernació per a residents
// ○ [6] Pressupost de la fusteria

La versió amb map és més curta, no necessita liniesA declarat abans, i sobretot diu el que fa: «cada tasca es converteix en una línia». Regla d'estil: si dins d'un forEach l'única cosa que fas és push a un array, el que volies era map.

  1. La taula comparativa

Aquesta és la taula de referència de la lliçó:

Forma Dóna índex? Es pot tallar? Què retorna? I amb await a dins?
for clàssic Sí (el portes tu) Sí (break, continue, return) Res , espera a cada volta
for...of No (fes servir entries()) Res , espera a cada volta
for...in Sí, però com a string Res Sí (però no el facis servir amb arrays)
forEach Sí (2n argument) No undefined No: no espera els callbacks
entries() + for...of Res
map Sí (2n argument) No Array nou No directament: produeix promeses

Les dues últimes columnes anticipen un problema real que resoldràs a Promeses i Async/Await. Avanço en dues línies: si dins d'un forEach hi poses un await, el forEach no espera i el programa continua abans que les operacions acabin; amb for...of, en canvi, cada volta espera l'anterior. Encara no cal que ho entenguis, però sí que sàpigues que l'elecció del bucle importarà quan arribi l'asincronia.

Com triar, en tres regles:

  1. Vas a transformar la llista en una altra llista? → map.
  2. Vas a interrompre el recorregut, o hi ha asincronia? → for...of (amb entries() si necessites l'índex).
  3. Només vols efectes sobre cada element, sense tallar? → forEach o for...of, al gust de l'equip.

  1. Recórrer arrays d'objectes i arrays imbricats

Un array d'objectes es recorre exactament igual; l'única cosa que canvia és que dins del bucle treballes amb propietats:

for (const tasca of backlog) {
  console.log(`${tasca.titol}: ${tasca.horesEstimades} h (${tasca.responsable})`);
}

Quan l'objecte conté al seu torn un array —com etiquetes— apareix un bucle dins d'un altre, la tècnica de 02-04:

const ambEtiquetes = [
  { id: 1, titol: 'Redissenyar la sala polivalent', etiquetes: ['espai', 'disseny'] },
  { id: 4, titol: 'Inventari de tintes de serigrafia', etiquetes: ['serigrafia', 'magatzem'] },
  { id: 7, titol: 'Revisar la guillotina', etiquetes: [] }
];

for (const tasca of ambEtiquetes) {
  if (tasca.etiquetes.length === 0) {
    console.log(`${tasca.titol}: sense etiquetes`);
    continue;
  }
  for (const etiqueta of tasca.etiquetes) {
    console.log(`${tasca.titol} → #${etiqueta}`);
  }
}
// Redissenyar la sala polivalent → #espai
// Redissenyar la sala polivalent → #disseny
// Inventari de tintes de serigrafia → #serigrafia
// Inventari de tintes de serigrafia → #magatzem
// Revisar la guillotina: sense etiquetes

Compte amb break en bucles imbricats: trenca només el bucle més intern. Si necessites sortir dels dos, fes servir una etiqueta de bucle (02-04) o extreu el doble bucle a una funció i fes servir return:

function cercarTascaAmbEtiqueta(tasques, cercada) {
  for (const tasca of tasques) {
    for (const etiqueta of tasca.etiquetes) {
      if (etiqueta === cercada) return tasca;      // surt dels DOS bucles
    }
  }
  return null;
}

console.log(cercarTascaAmbEtiqueta(ambEtiquetes, 'magatzem').id);   // 4

I per a una matriu (array d'arrays) el patró és el mateix:

const horesPerSetmana = [
  [8, 6, 7, 5, 4],      // setmana 1
  [6, 6, 8, 8, 2]       // setmana 2
];

for (const [s, setmana] of horesPerSetmana.entries()) {
  let total = 0;
  for (const hores of setmana) total += hores;
  console.log(`Setmana ${s + 1}: ${total} h`);
}
// Setmana 1: 30 h
// Setmana 2: 30 h

  1. L'error de mutar l'array mentre es recorre

Ja ho vam apuntar a 04-03; aquí toca entendre-ho bé, perquè cada forma de recórrer reacciona d'una manera diferent.

Esborrar mentre recorres amb índexs: els elements es desplacen i te'n saltes algun.

const tasques = ['a', 'b', 'b', 'c'];
for (let i = 0; i < tasques.length; i++) {
  if (tasques[i] === 'b') tasques.splice(i, 1);
}
console.log(tasques);     // [ 'a', 'b', 'c' ]   ✗ queda una 'b'

Esborrar dins d'un forEach: mateix problema, i a més forEach fixa el rang en començar, així que pot llegir posicions que ja no existeixen.

const tasques2 = ['a', 'b', 'b', 'c'];
tasques2.forEach((t, i) => { if (t === 'b') tasques2.splice(i, 1); });
console.log(tasques2);    // [ 'a', 'b', 'c' ]   ✗ igual de malament

Afegir dins d'un for...of: bucle infinit, perquè l'iterador continua trobant elements nous.

// ✗ NO EXECUTIS AIXÒ
// for (const t of tasques) { tasques.push(t); }

Les tres solucions correctes, en ordre de preferència:

// 1. La millor: no mutar, crear un array nou (filter, lliçó 04-05)
const senseBes = ['a', 'b', 'b', 'c'].filter((t) => t !== 'b');
console.log(senseBes);    // [ 'a', 'c' ]

// 2. Recórrer cap enrere: els índexs pendents no es mouen
const tasques3 = ['a', 'b', 'b', 'c'];
for (let i = tasques3.length - 1; i >= 0; i--) {
  if (tasques3[i] === 'b') tasques3.splice(i, 1);
}
console.log(tasques3);    // [ 'a', 'c' ]   ✓

// 3. Recórrer una còpia i modificar l'original
const tasques4 = ['a', 'b', 'b', 'c'];
for (const t of tasques4.slice()) {
  if (t === 'b') tasques4.splice(tasques4.indexOf(t), 1);
}
console.log(tasques4);    // [ 'a', 'c' ]   ✓

Regla d'or: l'array que estàs recorrent és de només lectura mentre dura el recorregut. Si l'has de canviar, produeix-ne un altre.

Un matís que sí que és segur: modificar els objectes de dins no canvia l'estructura de l'array i, per tant, no trenca el recorregut.

for (const tasca of backlog) {
  if (tasca.dataLimit < AVUI && tasca.estat !== 'feta') {
    tasca.prioritat = 'alta';      // ✓ segur: canvia l'objecte, no la llista
  }
}

  1. Exemple integrat: pintar el tauler de Taller Nómada

La Marta vol veure el tauler per consola, agrupat per estat, amb numeració i amb un peu de resum. Cada bloc del codi fa servir la forma de recórrer que millor li encaixa, i aquest és l'exercici real d'aquesta lliçó.

'use strict';

const AVUI = '2026-09-20';
const MARQUES = { pendent: '○', 'en-curs': '▸', feta: '✓' };
const ORDRE_ESTATS = ['en-curs', 'pendent', 'feta'];
const NOMS_ESTAT = { pendent: 'PENDENTS', 'en-curs': 'EN CURS', feta: 'ACABADES' };

/** Retorna la línia d'una tasca. Fa servir map + join per a les etiquetes. */
function liniaDeTasca(tasca, avui) {
  const vencuda = tasca.dataLimit < avui && tasca.estat !== 'feta';
  const etiquetes = tasca.etiquetes.map((e) => `#${e}`).join(' ');
  return `${MARQUES[tasca.estat]} [${tasca.id}] ${tasca.titol} · ${tasca.responsable} · ` +
         `${tasca.horesEstimades} h · ${etiquetes}${vencuda ? ' ⚠ VENÇUDA' : ''}`;
}

function pintarTauler(tasques, avui) {
  console.log(`═══ Tauler de Taller Nómada — ${avui} ═══`);

  // for...of sobre els estats: recorregut amb ordre controlat per nosaltres
  for (const estat of ORDRE_ESTATS) {
    // Aquí necessitem numeració dins de cada bloc: comptador explícit
    let numero = 0;
    const capcalera = NOMS_ESTAT[estat];
    const linies = [];

    // for...of sobre les tasques, amb continue per saltar les d'un altre estat
    for (const tasca of tasques) {
      if (tasca.estat !== estat) continue;
      numero++;
      linies.push(`  ${numero}. ${liniaDeTasca(tasca, avui)}`);
    }

    console.log(`\n${capcalera} (${linies.length})`);
    if (linies.length === 0) {
      console.log('  — sense tasques —');
      continue;
    }
    // forEach: només efectes, sense necessitat de tallar
    linies.forEach((linia) => console.log(linia));
  }

  // Peu de resum: acumuladors en un sol recorregut (patró de 02-02)
  let obertes = 0;
  let horesObertes = 0;
  let vencudes = 0;
  for (const tasca of tasques) {
    if (tasca.estat === 'feta') continue;
    obertes++;
    horesObertes += tasca.horesEstimades;
    if (tasca.dataLimit < avui) vencudes++;
  }

  console.log(`\n───────────────────────────────────────`);
  console.log(`${obertes} obertes · ${horesObertes} h pendents · ${vencudes} vençuda(es)`);
}

pintarTauler(backlogComplet, AVUI);

Sortida (amb el backlog canònic de 04-01):

═══ Tauler de Taller Nómada — 2026-09-20 ═══

EN CURS (2)
  1. ▸ [1] Redissenyar la sala polivalent · Iván · 12 h · #espai #disseny
  2. ▸ [5] Guia sobre enquadernació per a residents · Iván · 8 h · #enquadernació #documentació

PENDENTS (3)
  1. ○ [2] Cartelleria del taller de serigrafia · Marta · 6 h · #serigrafia #comunicació
  2. ○ [3] Actualitzar el web de reserves · Lucía · 14 h · #web #reserves
  3. ○ [6] Pressupost de la fusteria · Iván · 5 h · #fusteria #compres ⚠ VENÇUDA

ACABADES (1)
  1. ✓ [4] Inventari de tintes de serigrafia · Marta · 3 h · #serigrafia #magatzem

───────────────────────────────────────
5 obertes · 45 h pendents · 1 vençuda(es)

Repassa les decisions preses, perquè són el resum pràctic de la taula de l'apartat 9:

On Forma triada Per què
Recórrer els estats for...of amb continue Ordre fix, i cal saltar blocs
Filtrar tasques per estat for...of amb continue Cal interrompre amb continue i portar un comptador propi
Convertir etiquetes en #etiqueta map + join Transformació pura d'una llista en una altra
Imprimir les línies ja muntades forEach Només efectes, no hi ha res a tallar
Calcular el resum for...of amb acumuladors Tres totals en un sol recorregut

A la lliçó següent veuràs que aquell doble bucle de filtratge i aquell bloc d'acumuladors s'escriuen en dues línies amb filter i reduce. Però convé haver-ho fet abans a mà: els mètodes de 04-05 no són màgia, són aquests bucles amb nom.

Errors Habituals i Consells

1. Fer servir for...in amb arrays. Índexs com a string i propietats inesperades. Fes servir for...of.

2. Intentar un break dins d'un forEach. És un SyntaxError. Canvia a for...of, o fes servir find/some (04-05).

3. Creure que return dins d'un forEach talla el recorregut. Només acaba aquella volta; funciona com un continue.

4. Oblidar el return al callback de map. Obtens un array ple d'undefined. Recorda: fletxa amb claus → cal return.

5. Fer servir map pels seus efectes secundaris. Si no faràs servir l'array que retorna, el que volies era forEach:

backlog.map((t) => console.log(t.titol));      // ✗ crea un array d'undefined que ningú no fa servir
backlog.forEach((t) => console.log(t.titol));  // ✓

6. Mutar l'array mentre el recorres. Recorre cap enrere, recorre una còpia, o —molt millor— construeix un array nou.

7. Recórrer diverses vegades el mateix per comoditat. Si necessites tres totals, treu-los en un sol recorregut amb tres acumuladors, no en tres bucles. Amb sis tasques és irrellevant; amb vint mil no ho és (Mòdul 9).

Consell professional. Escriu el bucle pensant en qui el llegirà d'aquí a sis mesos. for (const tasca of backlog) s'entén sense pensar; for (let i = 0; i < b.length; i++) obliga a comprovar mentalment els límits. Reserva el for clàssic per als quatre casos en què el seu control extra és imprescindible: cap enrere, amb salt, aturant-se abans del final o comparant amb el veí.

Exercicis

Exercici 1 — Quatre recorreguts. Amb l'array const hores = [12, 6, 14, 3, 8, 5];:

  1. Mostra cada valor amb la seva posició fent servir entries().
  2. Calcula el total amb un for...of.
  3. Crea amb map un array enJornades amb les hores dividides entre 8, arrodonides a un decimal.
  4. Mostra només els tres primers valors, tallant el recorregut amb break.

Exercici 2 — Primer incompliment. Escriu primeraTascaSobrecarregada(tasques, limit) que retorni la primera tasca les horesEstimades de la qual superin el límit, o null si no n'hi ha cap. Ha de deixar de recórrer tan bon punt la trobi. Explica per què forEach no serveix aquí. Prova-la amb limit = 10 sobre el backlog.

Exercici 3 — Informe d'etiquetes per responsable. Amb el backlog canònic (camps responsable i etiquetes), escriu liniesPerResponsable(tasques) que retorni un array de strings, un per tasca, amb el format Iván · Redissenyar la sala polivalent · espai, disseny. Després escriu imprimirInforme(tasques) que l'imprimeixi numerat. Fes servir map per al primer i forEach amb índex per al segon, i justifica l'elecció.

Solucions

Exercici 1

const hores = [12, 6, 14, 3, 8, 5];

// 1. entries() dóna índex i valor, i permetria break
for (const parell of hores.entries()) {
  console.log(`Posició ${parell[0]}: ${parell[1]} h`);
}

// 2. Acumulador amb for...of
let total = 0;
for (const h of hores) total += h;
console.log(`Total: ${total} h`);        // Total: 48 h

// 3. map retorna un array nou de la mateixa longitud
const enJornades = hores.map((h) => Number((h / 8).toFixed(1)));
console.log(enJornades);                 // [ 1.5, 0.8, 1.8, 0.4, 1, 0.6 ]

// 4. break necessita un bucle de debò: for...of, no forEach
for (const [i, h] of hores.entries()) {
  if (i === 3) break;
  console.log(h);                        // 12, 6, 14
}

Les 48 h del punt 2 són el total canònic del backlog. Al punt 3, toFixed retorna un string, per això l'embolcallem en Number(...): és la conversió explícita de 01-07.

Exercici 2

function primeraTascaSobrecarregada(tasques, limit) {
  for (const tasca of tasques) {
    if (tasca.horesEstimades > limit) return tasca;   // surt del bucle i de la funció
  }
  return null;
}

const trobada = primeraTascaSobrecarregada(backlog, 10);
console.log(trobada?.titol ?? 'cap');                     // 'Redissenyar la sala polivalent'
console.log(primeraTascaSobrecarregada(backlog, 100));    // null

forEach no serveix per dos motius: no admet break, i un return dins del callback només acaba aquella volta, així que continuaria recorrent les sis tasques encara que la resposta sigui a la primera. Amb for...of i return surts del bucle i de la funció en la mateixa instrucció. A 04-05 veuràs que això té nom propi i una sola línia: tasques.find((t) => t.horesEstimades > limit).

Exercici 3

function liniesPerResponsable(tasques) {
  return tasques.map((tasca) =>
    `${tasca.responsable} · ${tasca.titol} · ${tasca.etiquetes.join(', ')}`
  );
}

function imprimirInforme(tasques) {
  const linies = liniesPerResponsable(tasques);
  linies.forEach((linia, i) => console.log(`${i + 1}. ${linia}`));
}

imprimirInforme(backlog);
// 1. Iván · Redissenyar la sala polivalent · espai, disseny
// 2. Marta · Cartelleria del taller de serigrafia · serigrafia, comunicació
// 3. Lucía · Actualitzar el web de reserves · web, reserves
// 4. Marta · Inventari de tintes de serigrafia · serigrafia, magatzem
// 5. Iván · Guia sobre enquadernació per a residents · enquadernació, documentació
// 6. Iván · Pressupost de la fusteria · fusteria, compres

La justificació de les eleccions és la regla de l'apartat 9. liniesPerResponsable transforma una llista de tasques en una llista de strings de la mateixa longitud: això és map per definició, i a més deixa la funció pura i provable al Mòdul 8. imprimirInforme només produeix efectes (imprimir) i necessita l'índex per numerar: forEach amb el seu segon argument encaixa exactament i no hi ha res a interrompre. Separar les dues coses —una funció que construeix el text i una altra que l'imprimeix— és la mateixa separació entre calcular i mostrar que vas aplicar a 03-01 amb descriureTasca.

Conclusió

Ja no recorres arrays per costum, sinó per decisió. Coneixes les sis formes i les seves regles: el for clàssic quan necessites control total del comptador (cap enrere, amb salt, aturant-te abans del final o mirant el veí); for...of com a opció per defecte, perquè elimina els índexs, admet break i continue i funciona amb qualsevol iterable; for...in reservat a objectes, mai a arrays, perquè dóna índexs en forma de string i arrossega propietats que no són elements; forEach per a efectes, amb índex còmode però sense possibilitat d'interrompre; entries() quan necessites índex i valor sense renunciar al break; i map quan el que vols no és recórrer, sinó produir una llista nova de la mateixa longitud.

La taula comparativa —dóna índex?, es pot tallar?, què retorna?, espera l'await?— resol l'elecció en segons, i la seva última columna et deixa avisat d'un problema que resoldràs a 05-06. Has vist també per què forEach no admet break (el bucle és fora de la teva funció, com va demostrar el teu propi elMeuForEach de 03-06), com recórrer arrays d'objectes i arrays imbricats sense perdre't, i per què mutar la llista mentre la recorres és una font inesgotable de bugs, amb les seves tres solucions. I has pintat el tauler de Taller Nómada triant a cada bloc la forma adequada, amb el resultat de sempre: 5 obertes, 45 h pendents, 1 vençuda.

Però fixa't en el que ha costat aquest resultat. Per agrupar per estat has escrit un bucle dins d'un altre amb un continue. Per al resum has necessitat tres variables acumuladores. Per trobar la primera tasca sobrecarregada has escrit una funció sencera. I per ordenar el tauler per data límit encara no tens cap eina. Tot això són operacions tan habituals que el llenguatge ja les porta resoltes i amb nom: cercar és find, comprovar és some i every, seleccionar és filter, ordenar és sort i acumular és reduce. És l'arsenal complet de Cercar, Ordenar i Agregar Dades: find, sort i reduce, on a més t'espera el parany més famós de JavaScript: per què [10, 9, 100].sort() no retorna el que et penses.

Curs de JavaScript: De Principiant a Avançat

Mòdul 1: Introducció a JavaScript

Mòdul 2: Estructures de Control

Mòdul 3: Funcions

Mòdul 4: Objectes i Arrays

Mòdul 5: Objectes i Funcions Avançades

Mòdul 6: El Model d'Objectes del Document (DOM)

Mòdul 7: APIs del Navegador i Temes Avançats

Mòdul 8: Proves i Depuració

Mòdul 9: Rendiment i Optimització

Mòdul 10: Frameworks i Llibreries de JavaScript

Mòdul 11: Projecte Final

© Copyright 2026. Tots els drets reservats