Saltar al contenido principal

Colecciones

Resumen de los diferentes tipos de colecciones en Dart.

Dart tiene soporte integrado para colecciones de tipo list, set y map. Para obtener más información sobre la configuración de los tipos que contienen las colecciones, consulta Genéricos.

Listas

#

Quizás la colección más común en casi todos los lenguajes de programación es el array, o grupo ordenado de objetos. En Dart, los arrays son objetos List, por lo que la mayoría de la gente simplemente los llama listas.

Los literales de listas en Dart se denotan mediante una lista de elementos separados por comas y encerrados entre corchetes ([]). Cada elemento suele ser una expresión. Aquí tienes una lista simple de Dart:

dart
var list = [1, 2, 3];

Puedes añadir una coma después del último elemento en un literal de colección de Dart. Esta coma final no afecta a la colección, pero puede ayudar a prevenir errores al copiar y pegar.

dart
var list = ['Car', 'Boat', 'Plane',];

Las listas usan indexación basada en cero, donde 0 es el índice del primer elemento y list.length - 1 es el índice del último elemento. Puedes obtener la longitud de una lista usando la propiedad .length y acceder a los elementos de una lista usando el operador de subíndice ([]):

dart
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);

list[1] = 1;
assert(list[1] == 1);

Para crear una lista que sea una constante en tiempo de compilación, añade const antes del literal de la lista:

dart
var constantList = const [1, 2, 3];
// constantList[1] = 1; // This line will cause an error.

Para obtener más información sobre las listas, consulta la sección Lists de la documentación de dart:core.

Sets

#

Un set en Dart es una colección desordenada de elementos únicos. El soporte de Dart para sets se proporciona mediante y el tipo Set.

Aquí tienes un set simple de Dart, creado usando un literal de set:

dart
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

Para crear un set vacío, usa {} precedido por un argumento de tipo, o asigna {} a una variable de tipo Set:

dart
var names = <String>{};
// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.

Añade elementos a un set existente usando los métodos add() o addAll():

dart
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);

Usa .length para obtener la cantidad de elementos en el set:

dart
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);

Para crear un set que sea una constante en tiempo de compilación, añade const antes del literal del set:

dart
final constantSet = const {
  'fluorine',
  'chlorine',
  'bromine',
  'iodine',
  'astatine',
};
// constantSet.add('helium'); // This line will cause an error.

Para obtener más información sobre los sets, consulta la sección Sets de la documentación de dart:core.

Maps

#

En un mapa, cada elemento es un par clave-valor. Cada clave dentro de un par está asociada con un valor, y tanto las claves como los valores pueden ser cualquier tipo de objeto. Cada clave puede aparecer solo una vez, aunque el mismo valor puede asociarse con múltiples claves diferentes. El soporte de Dart para mapas se proporciona mediante literales de mapa y el tipo Map.

Aquí tienes un par de mapas simples de Dart, creados usando literales de mapa:

dart
var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings',
};

var nobleGases = {2: 'helium', 10: 'neon', 18: 'argon'};

Puedes crear los mismos objetos usando un constructor de Map:

dart
var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

Añade un nuevo par clave-valor a un mapa existente usando el operador de asignación de subíndice ([]=):

dart
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair

Recupera un valor de un mapa usando el operador de subíndice ([]):

dart
var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');

Si buscas una clave que no está en el mapa, obtienes null a cambio:

dart
var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);

Usa .length para obtener la cantidad de pares clave-valor en el mapa:

dart
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);

Para crear un mapa que sea una constante en tiempo de compilación, añade const antes del literal del mapa:

dart
final constantMap = const {2: 'helium', 10: 'neon', 18: 'argon'};

// constantMap[2] = 'Helium'; // This line will cause an error.

Para obtener más información sobre los mapas, consulta la sección Maps de la documentación de dart:core.

Elementos de colección

#

Un literal de colección contiene una serie de elementos. Durante el tiempo de ejecución, cada elemento se evalúa, produciendo cero o más valores que luego se insertan en la colección resultante. Estos elementos se dividen en dos categorías principales: elementos hoja y elementos de flujo de control.

  • Elemento hoja: Inserta un elemento individual en un literal de colección.

    • Elemento de expresión: Evalúa una sola expresión e inserta el valor resultante en la colección.

    • Elemento de entrada de mapa: Evalúa un par de expresiones de clave y valor e inserta la entrada resultante en la colección.

  • Elemento de flujo de control: Añade condicional o iterativamente cero o más valores a la colección contenedora.

    • Elemento sensible a nulos (null-aware): Evalúa una expresión y, si el resultado no es null, inserta el valor en la colección contenedora.

    • Elemento de propagación (spread): Itera sobre una secuencia dada (expresión de colección) e inserta todos los valores resultantes en la colección contenedora.

    • Elemento spread null-aware: Similar al elemento de propagación, pero permite que la colección sea null y no inserta nada si lo es.

    • Elemento if: Evalúa condicionalmente un elemento interno basándose en una expresión de condición dada, y opcionalmente evalúa otro elemento else si la condición es falsa.

    • Elemento for: Itera y evalúa repetidamente un elemento interno dado, insertando cero o más valores resultantes.

Para obtener más información sobre los elementos de colección, consulta las siguientes secciones.

Elementos de expresión

#

Un elemento de expresión evalúa una sola expresión e inserta el valor resultante en una colección. Esta expresión puede abarcar varios constructos como literales, variables, operadores, llamadas a funciones y llamadas a constructores.

Un elemento de expresión tiene esta sintaxis en una colección:

dart
<expression>

Elementos de entrada de mapa

#

Un elemento de entrada de mapa evalúa un par de expresiones de clave y valor e inserta la entrada resultante en una colección. Tanto la clave como el valor dentro de este par pueden ser expresiones.

Un elemento de entrada de mapa tiene esta sintaxis en una colección:

dart
<key_expression>: <value_expression>

Elementos con conocimiento de nulidad

#

Un elemento sensible a nulos (null-aware) evalúa una expresión y, si el resultado no es null, inserta el valor en la colección contenedora.

Un elemento sensible a nulos (null-aware) tiene la siguiente sintaxis en un elemento de expresión:

dart
?<expression>

Un elemento sensible a nulos (null-aware) tiene la siguiente sintaxis en un elemento de entrada de mapa:

dart
// key is a null-aware element
?<key_expression>: <value_expression>
dart
// value is a null-aware element
<key_expression>: ?<value_expression>
dart
// key and value are null-aware elements
?<key_expression>: ?<value_expression>

En el siguiente ejemplo, el resultado para el elemento sensible a nulos ?absentValue no se añade a una lista llamada items porque absentValue es null:

dart
int? absentValue = null;
int? presentValue = 3;
var items = [
  1,
  ?absentValue,
  ?presentValue,
  absentValue,
  5,
]; // [1, 3, null, 5]

El siguiente ejemplo ilustra varias formas en que puedes usar elementos sensibles a nulos dentro de elementos de entrada de mapa:

dart
String? presentKey = 'Apple';
String? absentKey = null;

int? presentValue = 3;
int? absentValue = null;

var itemsA = {presentKey: absentValue}; // {Apple: null}
var itemsB = {presentKey: ?absentValue}; // {}

var itemsC = {absentKey: presentValue}; // {null: 3}
var itemsD = {?absentKey: presentValue}; // {}

var itemsE = {absentKey: absentValue}; // {null: null}
var itemsF = {?absentKey: ?absentValue}; // {}

Elementos de propagación (spread)

#

El elemento de propagación (spread) itera sobre una secuencia dada y añade todos los valores resultantes a la colección contenedora.

Un elemento de propagación (spread) tiene la siguiente sintaxis en una colección. La expresión de secuencia puede representar cualquier expresión que se evalúe como un objeto que implemente Iterable:

dart
...<sequence_expression>

En el siguiente ejemplo, los elementos de una lista llamada a se añaden a una lista llamada items.

dart
var a = [1, 2, null, 4];
var items = [0, ...a, 5]; // [0, 1, 2, null, 4, 5]

Si estás propagando una expresión que podría evaluarse como null y deseas ignorar el null (y no insertar elementos), usa un elemento de propagación sensible a nulos (null-aware spread).

Para obtener más información sobre el operador de propagación, consulta Operador de propagación (spread).

Elementos spread null-aware

#

El elemento spread null-aware es similar al elemento de propagación, pero permite que la colección sea null y no inserta nada si lo es.

Un elemento spread null-aware tiene esta sintaxis en una colección:

dart
...?<sequence_expression>

En el siguiente ejemplo, una lista llamada a se ignora porque es nula, pero los elementos de una lista llamada b se añaden a una lista llamada items. Ten en cuenta que si una colección en sí misma no es null, pero contiene elementos que lo son, esos elementos null se siguen añadiendo al resultado.

dart
List<int>? a = null;
var b = [1, null, 3];
var items = [0, ...?a, ...?b, 4]; // [0, 1, null, 3, 4]

Debido a la Null Safety, no puedes realizar una operación de propagación (...) sobre un valor que podría ser nulo. El siguiente ejemplo produce un error en tiempo de compilación porque el parámetro extraOptions es anulable y el operador de propagación utilizado en extraOptions no es null-aware.

análisis estático: fallo dart
List<String> buildCommandLine(
  String executable,
  List<String> options, [
  List<String>? extraOptions,
]) {
  return [
    executable,
    ...options,
    ...extraOptions, // <-- Error
  ];
}

// Usage:
//   buildCommandLine('dart', ['run', 'my_script.dart'], null);
// Result:
//   Compile-time error

Si deseas propagar una colección anulable, usa un elemento spread null-aware. El siguiente ejemplo es válido porque el operador de propagación null-aware se usa en extraOptions.

dart
List<String> buildCommandLine(
  String executable,
  List<String> options, [
  List<String>? extraOptions,
]) {
  return [
    executable,
    ...options,
    ...?extraOptions, // <-- OK now.
  ];
}

// Usage:
//   buildCommandLine('dart', ['run', 'my_script.dart'], null);
// Result:
//   [dart, run, my_script.dart]

Para obtener más información sobre el operador de propagación null-aware, consulta Operador de propagación (spread).

Elementos if

#

Un elemento if evalúa condicionalmente un elemento interno basándose en una expresión de condición dada, y opcionalmente evalúa otro elemento else si la condición es falsa.

El elemento if tiene algunas variaciones de sintaxis:

dart
// If the bool expression is true, include the result.
if (<bool_expression>) <result>
dart
// If the expression matches the pattern, include the result.
if (<expression> case <pattern>) <result>
dart
// If the operation resolves as true, include the first
// result, otherwise, include the second result.
if (<bool_expression>) <result> else <result>
dart
// If the operation resolves as true, include the first
// result, otherwise, include the second result.
if (<expression> case <pattern>) <result> else <result>

Los siguientes ejemplos ilustran varias formas en que puedes usar un elemento if dentro de una colección con una expresión booleana:

dart
var includeItem = true;
var items = [0, if (includeItem) 1, 2, 3]; // [0, 1, 2, 3]
dart
var includeItem = true;
var items = [0, if (!includeItem) 1, 2, 3]; // [0, 2, 3]
dart
var name = 'apple';
var items = [0, if (name == 'orange') 1 else 10, 2, 3]; // [0, 10, 2, 3]
dart
var name = 'apple';
var items = [
  0,
  if (name == 'kiwi') 1 else if (name == 'pear') 10,
  2,
  3,
]; // [0, 2, 3]

Los siguientes ejemplos ilustran varias formas en que puedes usar un elemento if con una parte case dentro de una colección:

dart
Object data = 123;
var typeInfo = [
  if (data case int i) 'Data is an integer: $i',
  if (data case String s) 'Data is a string: $s',
  if (data case bool b) 'Data is a boolean: $b',
  if (data case double d) 'Data is a double: $d',
]; // [Data is an integer: 123]
dart
var word = 'hello';
var items = [
  1,
  if (word case String(length: var wordLength)) wordLength,
  3,
]; // [1, 5, 3]
dart
var orderDetails = ['Apples', 12, ''];
var summary = [
  'Product: ${orderDetails[0]}',
  if (orderDetails case [_, int qty, _]) 'Quantity: $qty',
  if (orderDetails case [_, _, ''])
    'Delivery: Not Started'
  else
    'Delivery: In Progress',
]; // [Product: Apples, Quantity: 12, Delivery: Not Started]

Puedes mezclar diferentes operaciones if con una parte else if. Por ejemplo:

dart
var a = 'apple';
var b = 'orange';
var c = 'mango';
var items = [
  0,
  if (a == 'apple') 1 else if (a case 'mango') 10,
  if (b case 'pear') 2 else if (b == 'mango') 20,
  if (c case 'apple') 3 else if (c case 'mango') 30,
  4,
]; // [0, 1, 30, 4]

Para obtener más información sobre el condicional if, consulta la sentencia if. Para obtener más información sobre el condicional if-case, consulta la sentencia if-case.

Elementos for

#

Un elemento for itera y evalúa repetidamente un elemento interno dado, insertando cero o más valores resultantes.

Un elemento for tiene esta sintaxis en una colección:

dart
for (<expression> in <collection>) <result>
dart
for (<initialization_clause>; <condition_clause>; <increment_clause>) <result>

Los siguientes ejemplos ilustran varias formas en que puedes usar un elemento for dentro de una colección:

dart
var numbers = [2, 3, 4];
var items = [1, for (var n in numbers) n * n, 7]; // [1, 4, 9, 16, 7]
dart
var items = [1, for (var x = 5; x > 2; x--) x, 7]; // [1, 5, 4, 3, 7]
dart
var items = [1, for (var x = 2; x < 4; x++) x, 7]; // [1, 2, 3, 7]

Para obtener más información sobre el bucle for, consulta los bucles for.

Elementos de flujo de control anidados

#

Puedes anidar elementos de flujo de control entre sí. Esta es una alternativa potente a las comprensiones de listas (list comprehensions) en otros lenguajes.

En el siguiente ejemplo, solo los números pares en numbers se incluyen en items.

dart
var numbers = [1, 2, 3, 4, 5, 6, 7];
var items = [
  0,
  for (var n in numbers)
    if (n.isEven) n,
  8,
]; // [0, 2, 4, 6, 8]

Es común e idiomático usar una propagación (spread) en un literal de colección inmediatamente dentro de un elemento if o for. Por ejemplo:

dart
var items = [
  if (condition) oneThing(),
  if (condition) ...[multiple(), things()],
]; // [oneThing, [multiple_a, multiple_b], things]

Puedes anidar todo tipo de elementos a cualquier profundidad. En el siguiente ejemplo, los elementos if, for y de propagación (spread) se anidan entre sí en una colección:

dart
var nestItems = true;
var ys = [1, 2, 3, 4];
var items = [
  if (nestItems) ...[
    for (var x = 0; x < 3; x++)
      for (var y in ys)
        if (x < y) x + y * 10,
  ],
]; // [10, 20, 30, 40, 21, 31, 41, 32, 42]