Chapter 4. Handling Lists and Maps
In this chapter, the fundamentals of data handling with Dart are outlined. The aim of this chapter is to cover Lists and Maps that are used to provide foundational data structures for information handling in Dart.
If you are lucky enough to be familiar with other languages, then many of the concepts presented should be familiar. However, in case this is your first time seeing these techniques, the example pieces of code are self-contained. You may find it helpful to run and experiment with the examples to gain a feel of the workings of the language.
The chapter begins by discussing Lists, which are indexable data structures used to hold objects. Lists are very common elements in Dart, as they can be used in a variety of scenarios.
We also discuss how to use Maps, which are useful for handling key/value pairs. A key/value pair is an associative relationship where the key is used as an index to access a value. For example, you may have a months of the year data structure in which the key is a number and the value is derived from the number, e.g., 1 generates the month January and 12 denotes December.
Over the course of this chapter, you will learn how to utilize Maps and Lists within your application.
4.1 Creating Lists of Data
Solution
Use a List to organize objects as an ordered collection. A List represents an array object that can hold information. It provides a simple construct that uses a zero-indexed grouping of elements.
Hereâs an example of how to use a List in Dart:
void
main
()
{
List
listMonths
=
[
'January'
,
'February'
,
'March'
];
listMonths
.
forEach
(
);
}
Discussion
Lists are very versatile and can be used in a variety of circumstances. In the preceding example, a List
class is used to hold the months of the year, as shown in Figure 4-1.
The List
declaration is used to hold a String
, but it can actually hold a variety of data types, making this object extremely flexible. The List
class provides a number of helpful methods such as forEach
, length
, reverse
, isEmpty
, and isNotEmpty
. Each element within the List is directly addressable as well as being capable of being accessed via a forEach
method.
A List is denoted by the use of square start and end brackets. Within the square brackets are the List elements, separated by commas. List items can be initialized at declaration or amended at a later time during processing.
The length of the List is available as a method, and this is used to identify how many elements are currently available. Note the List is indexed from zero, so if you intend to manually access elements, you will need to use zero if you want the first element.
Another nice feature of Lists is that they include a range of methods to handle processing information. In the example, the forEach
method is used to perform a print
of the elements contained in the List.
As you become more confident with Dart, Lists will become one of the many tools that are essential in the applications you write.
4.2 Amending a List of Data
Solution
Use the List add
method to incorporate new content into a List. Lists support the dynamic addition of new elements and can be expanded as required.
Hereâs an example of how to add a List element in Dart:
void
main
()
{
List
listMonths
=
[
'January'
,
'February'
,
'March'
];
listMonths
.
add
(
'April'
);
listMonths
.
forEach
(
);
}
Discussion
In the preceding example code, a List is initially defined with three elements. If you want to expand the number of elements, this can be done by using the List add
method. The add
method will append the new element at the end of the List.
When you append a new element, as in Figure 4-2, the List
class takes care of all the associated processing. The method add
knows how to append the information passed and will ensure the relevant class properties (e.g., length
) are updated. Therefore, in the example, you would see the months output as âJanuary,â âFebruary,â âMarch,â âApril.â You will also see that the length of the List is amended to reflect that a fourth item has been added.
The dynamic nature of a List makes it perfect for multiple situations where data structure manipulation is required. You will see Lists used across a number of situations to handle a variety of data types.
4.3 Using Lists with Complex Types
Solution
Use Lists to organize the consolidation of other data types. Lists can be especially useful for handling other data structures such as Maps (see Recipe 4.4).
Hereâs an example of how to use a List with complex data types in Dart:
void
main
()
{
Map
<
String
,
dynamic
>
filmStarWars
=
{
"title"
:
"Star Wars"
,
"year"
:
1977
};
Map
<
String
,
dynamic
>
filmEmpire
=
{
"title"
:
"The Empire Strikes Back"
,
"year"
:
1980
};
Map
<
String
,
dynamic
>
filmJedi
=
{
"title"
:
"The Return of the Jedi"
,
"year"
:
1983
};
List
listFilms
=
[
filmStarWars
,
filmEmpire
,
filmJedi
];
Map
<
String
,
dynamic
>
currentFilm
=
listFilms
[
0
];
(
currentFilm
);
(
currentFilm
[
'title'
]);
}
Discussion
In the example, film data is added to a Map that encloses title and year information, as shown in Figure 4-3. Here we use a List to manage the individual Maps, so the individual elements can be combined. The resultant List provides a convenient data structure for accessing the information to be stored.
Accessing the information within the List follows the same process as a normal List. To access the information, you need to dereference the variable. In this context, to dereference, use an index to tell Dart that you want to access the property value. The listFilms[0]
means to access the first element in the List.
Once you have access to the List, the data type can then be accessed based on the associated data type. The example code uses a Map to allocate multiple values together. As each element is a Map, you now have the data associated with this. Use the dereferenced value to store in a new variable currentFilm
, which can be accessed directly or with a key.
Using Lists can provide an elegant method to access complex data types in a consistent manner. If you need to coordinate data types, consider using a List to make this process more manageable.
4.4 Handling Map Key/Value Pairings
Solution
Use a Map to handle key/value objects of any type. Map keys are required to be unique, as they act as the index to access Map values. Map values are not required to be unique and can be duplicated as needed.
Hereâs an example of how to declare a Map in Dart:
void
main
()
{
Map
<
int
,
String
>
mapMonths
=
{
0
:
'January'
,
1
:
'February'
,
2
:
'March'
};
Map
<
int
,
String
>
moreMonths
=
{
3
:
'April'
,
4
:
'May'
};
mapMonths
.
addEntries
(
moreMonths
.
entries
);
mapMonths
.
forEach
((
key
,
value
){
(
'
$
key
:
$
value
'
);
});
}
Discussion
In the code example, a Map is used to define a collection of data based on month information. A construct of this type is very useful to enable pieces of information to be combined together that benefit from a key/value pairing, as shown in Figure 4-4.
The month Map structure builds a relationship between the key and the value, e.g., Month 0 is January and January is Month 0. The structure of a Map is very useful in terms of processing list information, such as JavaScript Object Notation (JSON).
Declaration of the Map follows a standard variable format. Note: Map
is actually a function call, so this requires the addition of braces. In our example, we indicate that the Map will be composed of an integer and a string, which means a number is used for the key (i.e., index). The string value field holds the reference to the month. You could also define the value as dynamic, which would allow the use of different variable types, providing additional flexibility. To be more specific, you could introduce the dynamic definition to explicitly replace the string. If you make this change, Dart will automatically infer the correct data type based on the variable assignment.
To populate the Map, define a key (e.g., mapMonth
) and then assign a value (e.g., January
). The assignment of values can be made in any order; the important thing is to be careful not to duplicate the keys used.
Adding an element to the Map requires both a key and a value. In the example, an additional Map structure called aprilMonth
is created. Add the new month to the existing structure by calling the addEntries
method with the parameter of the newMonth
Map.
To access the information within the Map
class, use the Map methods and properties. A Map has a number of methods available that can be used to access the associated data items. To loop through each item, use the key to access the individual items. The example uses the Map forEach
method to access each data structure item. With access to the key, the information associated with the Map can be retrieved by combining these elements together.
Regarding access to data, it is always worth checking the style guide information provided by the Dart team. In this instance, rather than use a forEach
, you could also use a for
loop. The guidance associated with access is defined in the Dart documentation.
As the Dart language matures and guidelines are changed, it is good to be able to understand the reasoning behind them. The general tip to you the developer is to keep an awareness of these changes and look to respond to general guidance to avoid more complex refactoring in later development stages.
4.5 Printing Map Data Structure Content
Solution
Use a Map to reference an indexed item. Map values are referenced as key/value combinations, which can be assigned to a variable for easier access.
Hereâs an example of retrieving a Map property and outputting the value with Dart:
void
main
()
{
Map
<
int
,
String
>
mapMonths
=
{
0
:
'January'
,
1
:
'February'
,
2
:
'March'
};
(
"Month:
${
mapMonths
[
0
]
}
"
);
(
"Map:
$
mapMonths
"
);
}
Discussion
In the code example, the value within the Map structure is accessed via its integer key. When a value is required, we provide the key to index the Map and retrieve the desired value. The first print
statement explicitly requests the information associated with the key [0]
. The use of braces around the variable is required to ensure we output the value of the month. In the second print
statement, we output the entire Map for the mapMonths
variable.
If you donât want to create individual variables to hold the values, remember to dereference the Map values. In the print
statement the braces are added to indicate to the Dart compiler we want the variable values rather than the contents of the Map.
4.6 Validating That Content Exists in a Map
Solution
Use the indexing functionality of a Map to identify if a key explicitly exists.
Hereâs an example of validating that a key exists in a Map with Dart:
void
main
()
{
Map
<
int
,
String
>
mapMonths
=
{
0
:
'January'
,
1
:
'February'
,
2
:
'March'
};
if
(
mapMonths
[
0
]
!=
null
)
{
(
'Test 1: Key exists'
);
}
if
(
mapMonths
.
containsKey
(
2
))
{
(
'Test 2: Key exists'
);
}
}
Discussion
Maps are indexed using key values, so validating the existence of a key can quickly be performed. To find a key, use the required key to index the Map.
In the example, the Map will return a null
value where the key is not present in the Map. An explicit check can be performed to ascertain if the value exists by checking for a non-null
return. An alternative check would be to use the containsKey
method to verify the value exists. From a functional perspective, the if
statements are performing the same check.
Stylistically, you may prefer one approach over another. If the key exists in the Map, the information returned will be the value associated with the key.
4.7 Printing Complex Data Types
Solution
Use a print
statement to display information from an application. The print
statement will need to be formatted to correctly interpolate the data type to be displayed.
Hereâs an example of a how to print the complex data type:
import
'dart:convert'
;
void
main
()
{
// Create JSON value
Map
<
String
,
dynamic
>
data
=
{
jsonEncode
(
'title'
)
:
json
.
encode
(
'Star Wars'
),
jsonEncode
(
'year'
)
:
json
.
encode
(
1977
)
};
// Decode the JSON
Map
<
String
,
dynamic
>
items
=
json
.
decode
(
data
.
toString
());
(
items
);
(
items
[
'title'
]);
(
"This is the title:
$
items
['title']"
);
(
'This is the title:
${
items
[
'title'
]
}
'
);
}
Discussion
Use the $
character to reference a variable in a print
statement. Prefixing a variable with the $
tells Dart that a variable is being used and it should replace this value.
The following examples from the last two print
statements illustrate use cases that you will come across where the variable value needs a bit of help to be displayed correctly.
In the first use case, there is String
interpolation without braces:
(
"This is the title:
$
items
['title']"
);
In the second use case, there is String
interpolation with braces:
(
'This is the title:
${
items
[
'title'
]
}
'
);
The preceding statements look equivalent, but they are not. In the first print
statement, Dart will interpret the variable to be displayed as items. To display the variable correctly, it actually requires that it be enclosed in braces, as per the second line. Doing this will ensure that the value associated with items['title']
is correctly interpreted by the print
statement.
Get Flutter and Dart Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.