Dart



Dati: liste         

Diversamente da altri linguaggi Dart non ha una struttura array predefinita; questo può sorprendere ma fino ad un certo punto considerando che esistono possibilità alternative. In particolare Dart prevede due classi specifiche per questo scopo: le liste e le mappe. Cominceremo dalle liste che sono molto simili, concettualmente, agli array così come li conosciamo in altri linguaggi ma con qualche particolarità.
Le liste sono collezioni finite composte da 0 a n oggetti e indicizzate tramite sequenze di numeri interi. Gli indici vanno da 0 ad n-1, dove n è il numero degli elementi e determina la dimensione della lista. Fin qui nulla di nuovo per chi conosce un altro linguaggio di programmazione. Ogni lista viene valutata a runtime, a meno che non sia predefinita const, in tal caso la valutazione avverrà a compile time.
Un esempio veloce di lista lo possiamo trovare nell'esempio seguente:

main()
{
  var list01 = [1,2,3];
  print(list01 is List);
}

Alla seconda riga vediamo la classica definizione di una lista:

nomelista = [elemento-0, elemento-1, ...., elemento-n]

La terza ci presenta invece la classe base delle liste ovvero List.
Un'altra definizione, che crea una lista vuota (la prima) e una una lista con n elementi è formalmente la seguente:

1) List nomelista = new List();
2) List nomelista = new List(n);

In base alla natura dinamica del linguaggio è possibile mischiare elementi di diversa tipologia, quindi, ad esempio:

var list01 = [1, "aaa", 2, 'a']

è corretto.
Nell'esempio 1) qui sopra, la lista è dinamica, nel senso che è estensibile e riducibile. Nel secondo caso invece, dove la lunghezza permessa ed immutabile è data dal parametro intero n, ogni elemento della lista è inizializzato a null. Questa definizione di lista a lunghezza fissa, così come la definizione con esplicitazione immediata degli elementi, la prima che abbiamo dato, corrisponde al concetto di array di altri linguaggi.

esistono però anche altri sistemi di costruzione di una lista;

filled(n, x)
crea una nuova lista (di lunghezza fissa) composta da n elementi il cui valore è x. Ad esempio:
List list01 = new List.filled(3, 4);
crea la lista [4,4,4]

from(iterable)
crea una lista partendo da un'altra sequenza.
List list01 = new List.from(list02);
In questo caso è list01 è dinamica di deafult.

generate
è un po' più complicata e sarà più chiara più avanti nel corso, quando avremo appreso altri concetti. Per ora basti l'esempio, notando che anche in questo caso la lista creata è dinamica:

List list01= new List.generate(3, (int index) => index * index);

Abbiamo detto che le liste possono contenere oggetti di varia tipologia. Tuttavia se vogliamo limitare ad un tipo preciso gli elementi ammissibili possiamo farlo specificandolo in fase di dichiarazione, nella modalità che richiama la classe, inserendo il tipo desiderato all'interno di una coppia < >:

List<tipo> = new List();

  Esempio 4.1
1
2
3
4
5
6
7
8
main()
{
  List<int> list01 = new List();
  list01 = [1,2,3];
  list01.add(4);
  // list01.add("ciao");
  print(list01);
}

La riga 6 se de-commentata, dà origine ad un errore perchè alla riga 3 abbiamo specificato che vogliamo creare una lista di soli interi.

Come in altri linguaggi anche in Dart è l'operatore [] che permette di accedere ad un singolo elemento di una lista ed anche di modificarlo sotto il formato []=. Lo vedremo in azione in vari esempi. Per accedere ad un singolo elemento esiste anche il metodo elementAt(indice), usato di rado, per quanto ne so.
Una proprietà comune a tutti gli elementi di questo tipo è la lunghezza, che, abbiamo detto, che anche in Dart si esprime tramite la parola length. Attraverso di essa è anche possibile modificare la lunghezza della lista stessa (qualora la lista sia dinamica). Eventuali valori aggiunti saranno naturalmente inizializzati a null.
Ecco nel seguente esempio length in azione per modificare le dimensioni di una lista dinamica.

  Esempio 4.2
1
2
3
4
5
6
7
8
9
main()
{
  List list01 = new List();
  print(list01.length);
  list01.length = 5;
  print(list01.length);
  list01.length = 3;
  print(list01.length);
}

Una volta definita la lista dinamica ecco, alle righe 5 e 7 come possiamo aumentarne e ridurne le dimensioni. Nell'esempio 4.2 presentiamo di volata le altre proprietà:

  Esempio 4.3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
main()
{
var list01 = [1,2,3,4,5];
print(list01.length);
print(list01[0]);          // richiamo l'elemento avente indice 0
list01[2] = 9;             // assegnazione di un valore all'indice 2
print(list01[2]);         
print(list01.first);       // l'elemento all'indice 0 o errore se la lista è vuota
print(list01.last);        // l'elemento all'ultimo indice, errore se la lista è vuota
print(list01.isEmpty);     // true se la lista è vuota, false altrimenti
print(list01.isNotEmpty);  // true se la lista non è vuota, false altrimenti
print(list01.reversed);    // restituisce un sequenza della lista al contrario
// print(list01.single);      da usare solo la lista ha un solo elemento
// print(list01.iterator);    restituisce un iteratore - ne riparleremo
}

Le proprietà direi che sono in buona parte autodescrittive, single funziona solo se la lista ha un unico elemento, mentre iterable è un po' più complicata e ne riparleremo quando accenneremo agli iteratori. Interessante è reversed che restituisce una sequenza che potremo riversare, come vedremo tra breve in una lista con uno specifico metodo.
A proposito dei metodi, essi sono assai numerosi e ricoprono una ampia casistica operativa. Vediamo i metodi utili a risolvere le situazioni più comuni, rimandando alla documentazione sul sito ufficiale per ulteriori approfondimenti.

Aggiungere uno o più elementi in coda alla lista
Possibile solo con liste dinamiche, ovviamente, può essere fatto in due modi:
a) aggiungendo un elemento all'indice opportuno
b) preferibile - usando il metodo add()

  Esempio 4.4
1
2
3
4
5
6
7
8
9
10
main()
{
  List list01 = new List();
  list01 = [1,2,3];
  list01.length = 4;
  list01[3] = 4;
  print(list01);
  list01.add(5);
  print(list01);
}

Col sistema a) è necessario prima creare lo spazio (riga 5) e poi inserire il nuovo elemento (riga 6)
Col sistema b) la cosa si risolve più velocemente (riga 8).
Per inserire più elementi bisogna ricorrere ad un loop oppure al metodo addAll che inserisce in coda un elemento iterabile (vedremo cosa sono). Ad esempio:

var list02 = [1,2,3]
list01.addAll(list02);


Rimuovere uno o più elementi alla lista
Per questo scopo abbiamo più metodi a disposizione:

a) remove - elimina la prima occorrenza di un dato elemento restituendo true se l'elemento viene trovato o false altrimenti
b) removeAt(n) - elimina l'elemento ad un dato indice n. Il metodo restituisce l'elemento rimosso
c) removeLast - elimina l'ultimo elemento di una lista, anche in questo restituendolo
d) removeRange(x,y) - elimina gli elemente partendo dall'indice x fino all'indice y-1 (badate bene al -1).
e) removeWhere - elimina tutti gli elementi che soddisfano un certo predicato

naturalmente tutti questi metodi hanno senso su una lista dinamica, diversamente ne ricaveremo un messaggio di errore.

  Esempio 4.5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
main()
{
  List list01 = new List();
  list01 = [0,1,2,3,4,5,6,7,8,9,10,11,12];
  list01.remove(3);
  print(list01);
  list01.removeAt(0);
  print(list01);
  list01.removeLast();
  print(list01);
  list01.removeRange(1, 4);
  print(list01);
  list01.removeWhere((x) => x > 8);
  print(list01);
}

con il seguente output:

[0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12]
[1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12]
[1, 2, 4, 5, 6, 7, 8, 9, 10, 11]
[1, 6, 7, 8, 9, 10, 11]
[1, 6, 7, 8]

La riga 13 sarà più chiara più avanti, per ora prendetela semplicemente per buona. Ovviamente gli indici devono essere compresi tra i valori coerenti con la lista su cui si lavora e, per quanto riguarda range, seguendo la definizione al punto d) precedente, deve essere x <= y.

Se si vuole eliminare tutti gli elementi da lista, dinamica ovviamente, in un colpo solo si può ricorrere al metodo clear che riporta a 0 la lunghezza della lista.

list01.clear()

Inserire un elemento
Abbiamo due metodi:
insert(indice, elemento) - inserisce un certo elemento nella posizione specificata dall'indice
insertAll(indice, elemento iterable)

  Esempio 4.6
1
2
3
4
5
6
7
8
9
10
11
main()
{
  List list01 = new List();
  list01 = [0, "aa", 2, 'a'];
  print(list01);
  list01.insert(1, 99);
  print(list01);
  var list02 = [88,77,66];
  list01.insertAll(4, list02);
  print(list01);
}

output:

[0, aa, 2, a]
[0, 99, aa, 2, a]
[0, 99, aa, 2, 88, 77, 66, a]

Trovare un elemento
Anche in questo caso abbiamo due metodi indiretti ed uno diretto: (non si tratta degli unici modi per effettuare delle ricerche ma sono sicuramente molto semplici ed effcienti):

indexOf - trova la prima occorrenza di un elemento nella lista
lastIndexOf - trova l'ultima occorrenza di un elemento
In entrambi i casi è possibile indicare un indice di partenza per la ricerca, indice che di default è ovviamente 0.

  Esempio 4.7
1
2
3
4
5
6
7
8
9
main()
{
  List list01 = new List();
  list01 = [0,0,0,1,2,3,4,5];
  print(list01.indexOf(0));
  print(list01.indexOf(0,1));
  print(list01.lastIndexOf(0));
  print(list01.indexOf(44));
}

output:

0
1
2
-1

Interessante notare che -1 indica l'assenza dell'elemento cercato.

Alternativa è usare il metodo booleano (cioè che restituisce true o false) contains:

var list01 = [1,2,3];
print(list01.contains(1)); // true


Concludiamo questo utile paragrafo introducendo come ultimo metodo, a mio avviso utile ed interessante (li trovate comunque tutti nella pagina dedicata sul sito ufficiale del linguaggio)  join che riduce in formato stringa ogni elemento di una lista effettuandone successivamente una concatenazione, eventualmente inserendo anche un separatore:

  Esempio 4.8
1
2
3
4
5
6
7
8
9
main()
{
  var list01 = ['c', 'i', 'a', 'o'];
  String s1 = list01.join();
  print(s1);
  var list02 = [1,2,3,4];
  String s2 = list02.join('+');
  print(s2);
}

output:

ciao
1+2+3+4

Iterare sugli elementi di una collezione
Probabilmente il task più comune. il ciclo for, di cui parleremo nel paragrafo apposito, è il metodo più comune per lavorare sui singoli elementi:

  Esempio 4.9
1
2
3
4
5
6
main()
{
  var list01 = [1,2,3,4];
  for (var x in list01)
  print(x * x);
}

Questa è la strada più semplice ed è funzionale per la stragrande maggioranza degli scopi. Quando parleremo degli iteratori vedremo altre interessanti possibilità.