Why collections matter in console apps
Console apps often need to store multiple values entered over time: names, scores, prices, or commands. A collection is a container that holds many values of the same type so you can process them as a group: display them, search them, and compute summaries such as totals or averages.
In this chapter you will use two common collection types:
- Arrays (
T[]): fixed size once created. Great when you know the number of items up front. - List<T>: dynamic size. Great when you collect an unknown number of items from the user.
Arrays: fixed-size storage
An array has a length that cannot change after creation. You access elements by index (starting at 0). Arrays are efficient and simple when the number of items is known.
Create and fill an array
int[] scores = new int[3]; // length is 3, indices: 0..2
scores[0] = 10;
scores[1] = 20;
scores[2] = 15;Iterate an array
You can iterate with for when you need the index, or foreach when you only need the values.
// for: index available
for (int i = 0; i < scores.Length; i++)
{
Console.WriteLine($"Score #{i + 1}: {scores[i]}");
}
// foreach: value only
foreach (int s in scores)
{
Console.WriteLine($"Score: {s}");
}Searching in an array
For basic searching, a loop is often enough. Track whether you found the item and where.
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
int target = 20;
int foundIndex = -1;
for (int i = 0; i < scores.Length; i++)
{
if (scores[i] == target)
{
foundIndex = i;
break;
}
}
if (foundIndex != -1)
Console.WriteLine($"Found {target} at index {foundIndex}.");
else
Console.WriteLine("Not found.");Arrays do not support adding/removing items because their size is fixed. If you need to grow/shrink the collection, use List<T>.
List<T>: dynamic storage for user-driven input
List<T> grows as you add items. It supports common operations like add, remove, and search. This makes it ideal for interactive console apps where you do not know how many inputs the user will provide.
Core operations: Add, Remove, Search
var numbers = new List<int>();
// Add
numbers.Add(10);
numbers.Add(25);
// Search
bool has25 = numbers.Contains(25); // true
int indexOf25 = numbers.IndexOf(25); // 1
// Remove (first match)
numbers.Remove(10); // returns true if removed
// Remove at index
numbers.RemoveAt(0);Notes:
Containschecks whether an item exists.IndexOfreturns the index or-1if not found.Removeremoves the first matching value;RemoveAtremoves by index.
Iterating a List<T>
var items = new List<string> { "apple", "banana", "pear" };
for (int i = 0; i < items.Count; i++)
{
Console.WriteLine($"{i}: {items[i]}");
}
foreach (string item in items)
{
Console.WriteLine(item);
}Use Count for a list’s size (not Length).
Basic aggregation: sum, average, min, max
Aggregation means reducing many values into a single result. In console apps, you often compute totals and summary statistics from user input.
Compute sum and average
var values = new List<int> { 10, 20, 15 };
int sum = 0;
foreach (int v in values)
{
sum += v;
}
double average = values.Count > 0
? (double)sum / values.Count
: 0.0;
Console.WriteLine($"Sum: {sum}");
Console.WriteLine($"Average: {average:F2}");Compute min and max
Initialize min and max from the first element (when the list is not empty), then update as you scan.
var values = new List<int> { 10, 20, 15 };
if (values.Count == 0)
{
Console.WriteLine("No values.");
}
else
{
int min = values[0];
int max = values[0];
foreach (int v in values)
{
if (v < min) min = v;
if (v > max) max = v;
}
Console.WriteLine($"Min: {min}");
Console.WriteLine($"Max: {max}");
}Processing lists of strings
String collections are common for names, tags, and categories. Typical tasks include filtering, searching, and formatting output.
Search case-insensitively
Users may type different casing. Normalize both sides before comparing.
var names = new List<string> { "Alice", "bob", "CHARLIE" };
string query = "BoB";
bool found = false;
foreach (string n in names)
{
if (n.Trim().Equals(query.Trim(), StringComparison.OrdinalIgnoreCase))
{
found = true;
break;
}
}
Console.WriteLine(found ? "Match found" : "No match");Build a formatted, numbered list
var names = new List<string> { "Alice", "Bob", "Charlie" };
for (int i = 0; i < names.Count; i++)
{
Console.WriteLine($"{i + 1,2}. {names[i]}");
}The alignment component {i + 1,2} reserves 2 characters for the number, which helps columns line up.
Guided build: collect user entries, display them, compute summary statistics, print a report
You will build a small console workflow that collects numeric entries into a List<double>, then prints the entries and summary statistics (count, sum, average, min, max). This pattern appears in many real console utilities: logging measurements, tracking expenses, or recording scores.
Step 1: Create the list and collect entries
We will accept numbers until the user enters a blank line. Use double.TryParse to validate input and avoid crashes on invalid text.
var entries = new List<double>();
Console.WriteLine("Enter numbers (blank line to finish):");
while (true)
{
Console.Write("> ");
string? input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
break;
if (double.TryParse(input, out double value))
{
entries.Add(value);
}
else
{
Console.WriteLine("Please enter a valid number.");
}
}Step 2: Display what was collected
Show a numbered list so the user can verify the captured data.
Console.WriteLine();
Console.WriteLine("Entries:");
if (entries.Count == 0)
{
Console.WriteLine("(none)");
}
else
{
for (int i = 0; i < entries.Count; i++)
{
Console.WriteLine($"{i + 1,2}. {entries[i],10:F2}");
}
}{entries[i],10:F2} prints the number with 2 decimals and aligns it in a 10-character field.
Step 3: Compute summary statistics
Compute all statistics in a single pass through the list. This is efficient and keeps the logic clear.
int count = entries.Count;
double sum = 0;
double min = 0;
double max = 0;
if (count > 0)
{
min = entries[0];
max = entries[0];
foreach (double v in entries)
{
sum += v;
if (v < min) min = v;
if (v > max) max = v;
}
}
double average = count > 0 ? sum / count : 0.0;Step 4: Print a formatted report
Print a compact report that is easy to read. When there are no entries, keep the report valid and avoid misleading min/max values.
Console.WriteLine();
Console.WriteLine("Report");
Console.WriteLine("------");
Console.WriteLine($"Count : {count}");
Console.WriteLine($"Sum : {sum:F2}");
Console.WriteLine($"Average: {average:F2}");
if (count > 0)
{
Console.WriteLine($"Min : {min:F2}");
Console.WriteLine($"Max : {max:F2}");
}
else
{
Console.WriteLine("Min : (n/a)");
Console.WriteLine("Max : (n/a)");
}Optional extension: remove an entry by value
Many console tools let the user correct mistakes. This snippet removes the first matching value and then you can re-run the report logic.
Console.Write("Enter a value to remove (or blank to skip): ");
string? removeInput = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(removeInput) && double.TryParse(removeInput, out double toRemove))
{
bool removed = entries.Remove(toRemove);
Console.WriteLine(removed ? "Removed." : "Value not found.");
}Optional extension: collect and process a list of labels
If your app also needs text entries (for example, expense categories), you can collect them into List<string> and do simple processing like trimming, skipping blanks, and searching.
var labels = new List<string>();
Console.WriteLine("Enter labels (blank line to finish):");
while (true)
{
Console.Write("> ");
string? input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
break;
string cleaned = input.Trim();
labels.Add(cleaned);
}
Console.WriteLine("Labels:");
for (int i = 0; i < labels.Count; i++)
{
Console.WriteLine($"{i + 1}. {labels[i]}");
}