Exercise
Household accounts
Objetive
Write a C# program in C# that can store up to 10000 costs and revenues, to create a small domestic accounting system. For each expense (or income), should be allowed to save the following information:
• Date (8 characters: YYYYMMDD format)
• Description of expenditure or revenue
• Category
• Amount (if positive income, negative if an expense)
The program should allow the user to perform the following operations:
1 - Add a new expense (the date should "look right": day 01 to 31 months from 01 to 12 years between 1000 and 3000). The description must not be empty. Needless to validate the other data.
2 - Show all expenses of a certain category (eg, "studies") between two certain dates (eg between "20110101" and "20111231"). Number is displayed, date (format DD / MM / YYYY), description, category in parentheses, and amount to two decimal places, all in the same line, separated by hyphens. At the end of all data show the total amount of data displayed.
3 - Search costs containing a certain text (in the description or category without distinguishing case sensitive). Number is displayed, the date and description (the description is displayed in the sixth truncated blank, if any spaces six or more).
4 - Modify a tab (tab number prompt the user, it will show the previous value of each field and press Enter to not be able to modify any of the data). Should be advised (but not re-order) if the user enters a wrong card number. Needless to validate any data.
5 - Delete some data, from the number that you enter. Should be advised (but not re-order) if you enter an incorrect number. It should show the card to be clear and prompt prior to deletion.
6 - Sort data alphabetically, by date and (if matched) description.
7 - Normalize descriptions: remove trailing spaces, spaces and mirror sites. If a description is all uppercase, will be converted to lowercase (except for the first letter, kept in uppercase).
T-End the use of the application (as we store the information, the data will be lost).
Example Code
using System;
using System.Collections.Generic;
using System.Linq;
namespace HouseholdAccounts
{
class Program
{
// Create a structure to store cost/revenue information
struct AccountEntry
{
public string Date; // Date in YYYYMMDD format
public string Description; // Description of the expense or income
public string Category; // Category of the expense or income
public decimal Amount; // Amount (positive for income, negative for expense)
}
static List accountEntries = new List(); // List to store all account entries
static void Main(string[] args)
{
bool running = true; // Variable to control the main loop
while (running) // Main loop to keep the program running
{
Console.WriteLine("\nChoose an option:"); // Display menu options
Console.WriteLine("1 - Add new expense");
Console.WriteLine("2 - Show expenses by category");
Console.WriteLine("3 - Search costs by text");
Console.WriteLine("4 - Modify record");
Console.WriteLine("5 - Delete data");
Console.WriteLine("6 - Sort data");
Console.WriteLine("7 - Normalize descriptions");
Console.WriteLine("T - End application");
string choice = Console.ReadLine().ToUpper(); // Read user input and convert it to uppercase
switch (choice) // Switch based on user choice
{
case "1": // Add a new entry
// Ask user for date, description, category, and amount
Console.WriteLine("Enter the date (YYYYMMDD):");
string date = Console.ReadLine();
// Validate the date format (it should be YYYYMMDD, a valid date)
DateTime validDate;
while (!DateTime.TryParseExact(date, "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, out validDate))
{
Console.WriteLine("Invalid date. Please enter in the format YYYYMMDD:");
date = Console.ReadLine();
}
// Ask for description
Console.WriteLine("Enter description:");
string description = Console.ReadLine();
while (string.IsNullOrEmpty(description))
{
Console.WriteLine("Description cannot be empty. Please enter a description:");
description = Console.ReadLine();
}
// Ask for category
Console.WriteLine("Enter category:");
string category = Console.ReadLine();
// Ask for amount
Console.WriteLine("Enter amount (positive for income, negative for expense):");
decimal amount = Convert.ToDecimal(Console.ReadLine());
// Create a new account entry and add it to the list
AccountEntry entry = new AccountEntry
{
Date = date,
Description = description,
Category = category,
Amount = amount
};
accountEntries.Add(entry); // Add the entry to the list
break;
case "2": // Show expenses by category
Console.WriteLine("Enter start date (YYYYMMDD):");
string startDate = Console.ReadLine();
Console.WriteLine("Enter end date (YYYYMMDD):");
string endDate = Console.ReadLine();
// Filter the list by date range
var filteredEntries = accountEntries.Where(e =>
string.Compare(e.Date, startDate) >= 0 && string.Compare(e.Date, endDate) <= 0).ToList();
Console.WriteLine("Enter category:");
string categoryFilter = Console.ReadLine();
// Filter by category
filteredEntries = filteredEntries.Where(e => e.Category.Equals(categoryFilter, StringComparison.OrdinalIgnoreCase)).ToList();
// Display the filtered records
decimal totalAmount = 0;
int count = 1;
foreach (var entryInList in filteredEntries) // Changed variable name to avoid conflict
{
// Format and display each entry
Console.WriteLine($"{count} - {entryInList.Date.Substring(6, 2)}/{entryInList.Date.Substring(4, 2)}/{entryInList.Date.Substring(0, 4)} - {entryInList.Description} - ({entryInList.Category}) - {entryInList.Amount:F2}");
totalAmount += entryInList.Amount;
count++;
}
Console.WriteLine($"Total amount: {totalAmount:F2}");
break;
case "3": // Search costs by text
Console.WriteLine("Enter text to search:");
string searchText = Console.ReadLine().ToLower();
// Find entries that match the search text in either description or category (case insensitive)
var searchResults = accountEntries.Where(e =>
e.Description.ToLower().Contains(searchText) || e.Category.ToLower().Contains(searchText)).ToList();
if (searchResults.Count == 0)
{
Console.WriteLine("No records found.");
}
else
{
int searchCount = 1;
foreach (var entryInList in searchResults) // Changed variable name to avoid conflict
{
// Display the result with description truncated if necessary
string descriptionSearch = entryInList.Description.Length > 50 ? entryInList.Description.Substring(0, 50) + "..." : entryInList.Description;
Console.WriteLine($"{searchCount} - {entryInList.Date.Substring(6, 2)}/{entryInList.Date.Substring(4, 2)}/{entryInList.Date.Substring(0, 4)} - {descriptionSearch}");
searchCount++;
}
}
break;
case "4": // Modify a record
Console.WriteLine("Enter the record number to modify:");
int recordNumber = Convert.ToInt32(Console.ReadLine()) - 1;
if (recordNumber < 0 || recordNumber >= accountEntries.Count)
{
Console.WriteLine("Invalid record number.");
break;
}
// Show current values for modification
AccountEntry modifyEntry = accountEntries[recordNumber];
Console.WriteLine($"Current values: Date: {modifyEntry.Date}, Description: {modifyEntry.Description}, Category: {modifyEntry.Category}, Amount: {modifyEntry.Amount}");
// Modify each field
Console.WriteLine("Enter new date (or press Enter to keep current):");
string newDate = Console.ReadLine();
if (!string.IsNullOrEmpty(newDate)) modifyEntry.Date = newDate;
Console.WriteLine("Enter new description (or press Enter to keep current):");
string newDescription = Console.ReadLine();
if (!string.IsNullOrEmpty(newDescription)) modifyEntry.Description = newDescription;
Console.WriteLine("Enter new category (or press Enter to keep current):");
string newCategory = Console.ReadLine();
if (!string.IsNullOrEmpty(newCategory)) modifyEntry.Category = newCategory;
Console.WriteLine("Enter new amount (or press Enter to keep current):");
string newAmount = Console.ReadLine();
if (!string.IsNullOrEmpty(newAmount)) modifyEntry.Amount = Convert.ToDecimal(newAmount);
// Save the modified entry
accountEntries[recordNumber] = modifyEntry;
break;
case "5": // Delete data
Console.WriteLine("Enter start record number to delete:");
int startRecord = Convert.ToInt32(Console.ReadLine()) - 1;
Console.WriteLine("Enter end record number to delete:");
int endRecord = Convert.ToInt32(Console.ReadLine()) - 1;
if (startRecord < 0 || endRecord >= accountEntries.Count || startRecord > endRecord)
{
Console.WriteLine("Invalid record numbers.");
break;
}
// Confirm deletion of records
for (int i = startRecord; i <= endRecord; i++)
{
Console.WriteLine($"Are you sure you want to delete record {i + 1}? (Y/N)");
string confirmation = Console.ReadLine().ToUpper();
if (confirmation == "Y")
{
accountEntries.RemoveAt(i);
i--; // Adjust for removed entry
}
}
break;
case "6": // Sort data
var sortedEntries = accountEntries.OrderBy(e => e.Date).ThenBy(e => e.Description).ToList();
// Display sorted entries
int sortedCount = 1;
foreach (var entryInList in sortedEntries) // Changed variable name to avoid conflict
{
Console.WriteLine($"{sortedCount} - {entryInList.Date.Substring(6, 2)}/{entryInList.Date.Substring(4, 2)}/{entryInList.Date.Substring(0, 4)} - {entryInList.Description} - ({entryInList.Category}) - {entryInList.Amount:F2}");
sortedCount++;
}
break;
case "7": // Normalize descriptions
for (int i = 0; i < accountEntries.Count; i++) // Use a for loop to modify the list directly
{
var entryInList = accountEntries[i]; // Get the entry at index i
string normalizedDescription = entryInList.Description.Trim();
if (normalizedDescription.ToUpper() == normalizedDescription) // If the description is all uppercase
{
normalizedDescription = char.ToUpper(normalizedDescription[0]) + normalizedDescription.Substring(1).ToLower();
}
// Update the description in the entry
accountEntries[i] = new AccountEntry
{
Date = entryInList.Date,
Description = normalizedDescription,
Category = entryInList.Category,
Amount = entryInList.Amount
};
}
break;
case "T": // End application
running = false;
break;
default:
Console.WriteLine("Invalid option."); // Handle invalid input
break;
}
}
}
}
}