diegoRodicio

View Categories

🔄 Coleccións e funcións

Tempo de lectura estimado: 5 minutos

🔔 Que función recibe cada operación?

Algunhas operacións reciben un callback. Nós indicamos que facer cun elemento (ou como comparar dous) e a operación encárgase de chamar á función.

Podemos pasar unha función polo seu nome, sen (), ou escribir unha función anónima directamente nos argumentos. Non chamamos nós á función: entregámola para que a operación a use.

OperaciónQue recibe o callback?Que debe devolver?
map()Un elementoO valor transformado
where()Un elementotrue para conservalo ou false para descartalo
sort()Dous elementosUn enteiro que indica a orde entre eles

🔄 map(): transformar cada elemento

O método map() recibe un callback que transforma cada elemento. Por exemplo, recibe un nome e devolve ese nome en maiúsculas:

String converterMaiusculas(String nome) {
  return nome.toUpperCase();
}

void main() {
  final alumnos = ['Xurxo', 'Roi', 'Iria'];
  // Pasamos a función: map chamaraa cos elementos da lista.
  final nomes = alumnos.map(converterMaiusculas).toList();
  print(nomes); // [XURXO, ROI, IRIA]
}

Tamén podemos escribir a función no propio argumento:

final alumnos = ['Xurxo', 'Roi', 'Iria'];
final nomes = alumnos
    .map((String nome) => nome.toUpperCase())
    .toList();
print(nomes); // O mesmo resultado que coa función con nome.

O callback (String nome) => nome.toUpperCase()

recibe cada nome e devolve ese texto en maiúsculas. Ao percorrer o resultado, por exemplo con toList(), execútase o callback para cada elemento. Tamén podemos cambiar o tipo: .map((nome) => nome.length) transforma textos en números.

map() non cambia a lista orixinal. Devolve un Iterable, unha secuencia que podemos percorrer; .toList() percorre esa secuencia e crea unha lista cos resultados. A transformación aplícase ao percorrer os elementos.

O método map() transforma datos; o tipo Map garda claves e valores. Son cousas distintas.

🧩 Un callback que captura un desconto

Os conceptos anteriores non son excluíntes. Unha mesma función pode ter as tres características:

void main() {
  final prezos = [10.0, 25.0, 40.0];
  double desconto = 0.20;

  final prezosRebaixados = prezos
      .map((double prezo) => prezo * (1 - desconto))
      .toList();

  print(prezosRebaixados);
  // [8.0, 20.0, 32.0]
}

A función (double prezo) => prezo * (1 - desconto) é simultaneamente:

  • unha función anónima, porque non ten nome;
  • un callback, porque se pasa como argumento a map();
  • unha closure, porque captura a variable exterior desconto.

🔍 where(): conservar os elementos que cumpren unha condición

O método where() emprega un callback que debe devolver true ou false. Non transforma o elemento: decide se se conserva.

bool estaAprobada(int nota) {
  return nota >= 5; // true: conservar; false: descartar.
}

void main() {
  final notas = [4, 7, 9];
  final aprobadas = notas.where(estaAprobada).toList();
  print(aprobadas); // [7, 9]
}

A mesma operación cunha función anónima:

void main() {
  final notas = [3, 7, 5, 9, 4];

  final aprobadas = notas
      .where((int nota) => nota >= 5)
      .toList();

  print(aprobadas); // [7, 5, 9]
}

O callback execútase para cada nota:

  • se devolve true, a nota inclúese no resultado;
  • se devolve false, descártase.

Como map(), where() devolve un Iterable e non modifica a lista orixinal. Usamos toList() para obter a nova lista.

🔡 sort(): ordenar a propia lista

sort() cambia a orde dos elementos da lista existente. Non devolve unha lista nova: chamámolo e despois usamos a mesma lista.

Con números ou textos podemos usar sort() sen argumentos para a orde predeterminada. Por exemplo, [4, 9, 7] pasa a [4, 7, 9].

Para escoller a orde, pasamos un comparador. Recibe dous elementos, a e b, e debe devolver:

ResultadoSignificado
Un número negativoa debe ir antes ca b.
CeroSon equivalentes para esta ordenación.
Un número positivoa debe ir despois de b.

compareTo() xa devolve un enteiro con ese significado:

int compararNotas(int a, int b) {
  return a.compareTo(b); // Orde de menor a maior.
}

void main() {
  final notas = [4, 9, 7];
  notas.sort(compararNotas); // Pasamos o comparador polo nome.
  print(notas); // [4, 7, 9]

  notas.sort((a, b) => b.compareTo(a)); // Invertemos a comparación.
  print(notas); // [9, 7, 4]
}

Por exemplo, 4.compareTo(9) é negativo: o 4 vai antes do 9. O comparador pode chamarse varias veces coas parellas que necesite a ordenación. Non debe devolver true ou false, senón un int.

Se queremos ordenar sen modificar a lista orixinal, primeiro facemos unha copia:

final notas = [4, 9, 7];
final ordenadas = List<int>.of(notas); // Creamos outra lista.
ordenadas.sort();
print(notas); // [4, 9, 7]
print(ordenadas); // [4, 7, 9]

Tamén podemos ordenar rexistros polo campo que nos interese:

final alumnos = [(nome: 'Roi', nota: 6), (nome: 'Iria', nota: 9)];
alumnos.sort((a, b) => b.nota.compareTo(a.nota)); // Maior nota primeiro.
print(alumnos);

🔔 forEach(): facer unha acción por elemento

Recibe un callback e execútao unha vez por elemento. A acción non ten que devolver un resultado: forEach() non crea unha colección cos valores devoltos.

void saudarAlumno(String nome) {
  print('Ola, $nome'); // Acción que facemos con cada alumno.
}

void main() {
  final alumnos = ['Xurxo', 'Roi', 'Iria'];
  alumnos.forEach(saudarAlumno); // Pasamos a función polo nome.
}

A mesma operación cunha función anónima:

void main() {
  final alumnos = ['Xurxo', 'Roi', 'Iria'];
  alumnos.forEach((String nome) {
    print('Ola, $nome'); // Unha chamada por alumno.
  });
}

Para percorrer unha colección e executar instrucións tamén podemos usar for-in, que adoita resultar claro. Escolle map() se queres transformar datos e obter os resultados, e forEach() se só queres facer unha acción por elemento.

🏗️ Construtores de listas

Ademais de escribir [...], podemos crear listas con estas formas, chamadas construtores.

🔁 List.filled(): repetir un valor

final numeros = List<int>.filled(5, 4); // Cinco elementos co valor 4.
print(numeros); // [4, 4, 4, 4, 4]

numeros[0] = 8; // Podemos cambiar un elemento.
// numeros.add(4); // Fallaría: por defecto, o tamaño é fixo.
print(numeros); // [8, 4, 4, 4, 4]

O primeiro argumento indica a cantidade de elementos e o segundo, o valor inicial. Para gardar o texto '4', usariamos List<String>.filled(5, '4').

Se queremos poder engadir ou eliminar elementos, indicamos growable: true:

final numeros = List<int>.filled(5, 4, growable: true);
numeros.add(9); // Agora a lista pode aumentar de tamaño.
print(numeros); // [4, 4, 4, 4, 4, 9]

final impide asignar outra lista á variable; growable indica se podemos cambiar o tamaño da lista. Son cousas distintas.

🔢 List.generate(): calcular cada elemento

Recibe a cantidade e unha función que recibe o índice, empezando en cero, e devolve o valor desa posición:

final numeros = List<int>.generate(5, (indice) => indice + 1);
print(numeros); // [1, 2, 3, 4, 5]

A función recibe 0, 1, 2, 3 e 4; ao sumar un, xera os valores do 1 ao 5. Tamén se pode pasar unha función definida fóra, polo nome, como en map(). Por defecto esta lista pode cambiar de tamaño; podemos indicar growable: false.

Coidado ao repetir listas: List.filled() repite o mesmo valor. Se ese valor é unha lista modificable, todas as posicións comparten esa lista. Para crear unha lista independente en cada posición, usa List.generate():

final grupos = List<List<String>>.generate(2, (_) => <String>[]);
// Ignoramos o índice con _: só queremos unha lista nova cada vez.
grupos[0].add('Xurxo');
print(grupos); // [[Xurxo], []]

📋 List.of(): facer unha copia

List<int>.of(notas) crea outra lista cos elementos de notas, como vimos en sort(). Copia a lista, pero non crea copias independentes dos obxectos que contén.

🧱 Construír listas con ..., if e for

Estas formas serán útiles para crear listas de widgets en Flutter:

  • ...lista engade os seus elementos, non a lista como un único elemento.
  • ...?lista fai o mesmo, pero non engade nada se a lista é null.
  • if inclúe un elemento segundo unha condición.
  • for xera elementos percorrendo outra colección.

🧪 Exemplo completo

🔗 Ver o código en DartPad — https://dartpad.dev/?id=fb37221c89b484634cf0b2b2b46ff2e2

📚 Consulta: map — https://api.dart.dev/dart-core/Iterable/map.html, where — https://api.dart.dev/dart-core/Iterable/where.html e sort — https://api.dart.dev/dart-core/List/sort.html.

📚 Construtores: List.filled — https://api.dart.dev/dart-core/List/List.filled.html, List.generate — https://api.dart.dev/dart-core/List/List.generate.html e List.of — https://api.dart.dev/dart-core/List/List.of.html.