DEV Community

Cover image for Conociendo las listas en Dart
Alfredo Bautista 💙 #FlutterConf20
Alfredo Bautista 💙 #FlutterConf20

Posted on

Conociendo las listas en Dart

¡Buenas a todos! En este post conoceremos un poco más la clase List para trabajar de forma más sencilla y eficiente con ella.

Single Level of Abstraction (SLA) 🔮

SLA o principio de mismo nivel de abstracción nos indica, que en un código limpio, no debe mezclar instrucciones de alto nivel con instrucciones de bajo nivel en la implementación de nuestra lógica. Esta norma genera código que es legible y entendible al no mezclar niveles de profundidad dentro de un mismo método o fragmento de código.

Podemos diferenciar entre estructuras de bajo nivel o alto nivel cuando nuestro código debe utilizar índices numéricos, bucles for y while, comprobación manual de propiedades, etc.

Estructuras de alto nivel pueden ser métodos descriptivos que cumplan una función específica como la búsqueda por nombre, la ordenación por un criterio o el filtrado de datos.

Un ejemplo claro de estructuras de bajo nivel es la implementación de una búsqueda por nombre de un usuario, en un Array, usando bucles con índices y redirección de flujo. En este caso en Java.


 public User userByName(User[] array, String name) {

       User user = null;

        for (int i = 0; i < array.length; i++) {
             if(array[i].name == name){
                user = array[i];
                break;
            }
        }

        return user;
    }

En los lenguajes de alto nivel ya tenemos métodos, creados por los desarrolladores del lenguaje, para que podamos simplificar estos procesos y escribir todo nuestro código en un nivel alto de abstracción, sin tener que trabajar con estructuras de bajo nivel. Aquí tenemos una búsqueda, por nombre de usuario, en una lista usando estructuras de alto nivel, escrito en Dart.

 User userByName(List<User> array, String name){
     return array.firstWhere((User user) => user.name == name);
  }

Métodos útiles de Listas de Dart

Ahora que ya hemos visto porque utilizar funcionalidades del propio lenguaje que tener que codificarlo nosotros, vamos a ver qué métodos útiles nos da List en Dart.

map

El método map nos devuelve un nuevo iterable, con el resultado que le asignemos en el callback, que le pasamos al método. En este caso, queremos una lista que nos indique: Username: + nombre del usuario

List<String> nameOfUsers = users.map((user) => 'Username: ' + user.name).toList();

print(nameOfUsers); // [Username: Alfredo, Username: pepito, Username: Otro]

where y firstWhere

Los métodos de búsqueda o filtro where y firstWhere nos devuelven un iterable o ítem, que cumpla con el criterio indicado en la callback.

var usersOlderThan18 = users.where((user) => user.age >= 18).toList();

var firstUserOlderThan18 = users.firstWhere((user) => user.age >= 18);

contains

Este método nos devuelve un boolean indicando, si la lista contiene el mismo ítem que le pasamos por parámetro. Es importante recalcar que debe ser la misma instancia, no un ítem que sea igual.

Por ejemplo, este código nos devuelve false

 List<User> users = [User(name: 'Alfredo'), User(name: 'Pepito')];

 print(users.contains(User(name: 'Alfredo'))) // false;

este código nos devuelve true al ser la misma instancia

var usuario = User(name: 'Usuario');

users.add(usuario);

print(users.contains(usuario)) // true;

any

Nos devuelve un boolean, indicando si algún ítem cumple el criterio especificado en la callback.

users.any((user) => user.age > 18);

remove, removeAt, removeWhere

Estos métodos nos sirven para eliminar ítems de nuestra lista, es importante recalcar que muta o modifica la lista original, algunos ejemplos de ello:

  • remove: Nos permite eliminar el ítem pasándole la misma instancia por parámetro, como hemos visto anteriormente, debe ser el mismo ítem, no uno igual.
var userTemp = users[0];

users.remove(userTemp);
  • removeAt: Nos permite eliminar el ítem sabiendo su posición, sin necesidad de tener la instancia del mismo.
users.removeAt(0);
  • removeWhere: Este último nos permite eliminar el ítem sin necesidad de tener su instancia o conocer su posición. Eliminará todos los elementos que cumplan con la condición de la callback.
users.removeWhere((user) => user.name == 'Alfredo');

sort

Este método nos permite ordenar las listas, para ello utiliza Comparator si el ítem lo contiene o podemos pasarle en la callback la función que va a utilizar para saber cómo ordenar los elementos.

Hay clases del lenguaje que ya contienen un comparador y puede ordenar las listas automáticamente, como por ejemplos los números.

List<int> nums = [13, 2, -11];
nums.sort();
print(nums);  // [-11, 2, 13]

Si no tenemos Comparator implementado debemos retornar un integer en la callback con la siguiente especificación:

  • Sí se debe ordenar por encima, un valor positivo.
  • Sí son iguales un 0.
  • Sí es menor un valor negativo.
List<User> users = [User(name: 'Alfredo', age: 22), User(name: 'Pepito', age: 19), User(name: 'Otro', age: 25)];

users.sort((userA, userB) => userA.age >= userB.age ? 0 : 1);

print(users); // [Otro, Alfredo, Pepito]

Si queremos que estén ordenados de menor a mayor:


users.sort((userA, userB) => userA.age >= userB.age ? 1 : 0);

print(users); // [Pepito, Alfredo, Otro]

indexWhere

Este método nos devuelve el indice del ítem que cumpla la condición del callback, es muy útil para buscar cuándo no tenemos la instancia del objeto y queremos su posición.

users.indexWhere((user) => user.name == 'Alfredo');

Propiedades útiles

En la clase List tenemos propiedades interesantes como reversed que nos devuelve un iterador con el contenido de la lista en un orden inverso.


List <User> usersInverted = users.reversed.toList();

users // Alfredo, Pepito, Otro
usersInverted // Otro, Pepito, Alfredo

U otras como isEmpty o isNotEmpty que nos devuelve un boolean, indicando si la lista contiene algún elemento.

💡 Tip: construir una lista inmutable

En Dart, tenemos el keyword final que nos permite indicar que una variable no va a cambiar de tipo, pero esto no nos asegura la integridad de una lista. Al añadir o quitar un elemento la lista sigue teniendo el mismo tipo aunque su contenido haya cambiado.

final List<User> users = [User(name: 'Alfredo', age: 22), User(name: 'Pepito', age: 19), User(name: 'Otro', age: 25)];

users.add(User(name: 'Nuevo', age: 17));

users // [Alfredo, Pepito, Otro, Nuevo];

Para evitar esto tenemos el constructor List.unmodifiable que restringe que nuestra lista no permita añadir o remover elementos.

final List<User> users = List.unmodifiable([User(name: 'Alfredo', age: 22), User(name: 'Pepito', age: 19), User(name: 'Otro', age: 25)]);

users.add(User(name: 'Nuevo', age: 17)); // Uncaught Error: Unsupported operation: add

users.removeAt(0); // Uncaught Error: Unsupported operation: removeAt

Resumen

Con estos pocos métodos y utilidades podremos trabajar de una forma más rápida y generando código sencillo de leer y mantener usando las listas.

En este enlace os dejo toda la documentación de la clase List en el momento de escribir el post.

Recordad que usando extensions methods introducidos en Dart 2.7 podemos implementar nuestros propios métodos en la clase List y así mejorar la simplicidad de nuestro código, os dejo este enlace por si no conocéis las extensiones en Dart.

¡Un saludo y hasta la próxima!

Top comments (0)