How to choose different elements within an array of objects in c# - c#

I have an Invoice class and a main class. I have an array of objects within my main class and I want to multiply the quantity(7) by the price(57.88). How do you select different elements within an object array. I have shown my main and my invoice classes. I'm looking for an example on how I would go about this pertaining to what I have done with my code.
static void Main(string[] args)
{
var tools = new[]
{
new Invoice(83,"Electric Sander", 7, 57.88M),
new Invoice(24, "Power Saw", 18, 99.99M),
new Invoice(7, "Sledge Hammer", 11, 21.50M),
new Invoice(77, "Hammer", 76, 11.99M),
new Invoice(39, "Lawn Mower", 3, 79.50M),
new Invoice(68, "Screwdriver", 106, 6.99M),
new Invoice(56, "Jig Saw", 21, 11.00M),
new Invoice(3, "Wrench", 34, 7.50M)
};
Console.WriteLine("Original Array: ");
foreach (var tool in tools)
{
Console.WriteLine(tool);
}
var descriptionSort = from t in tools
orderby t.PartDescription
select t;
}
public class Invoice
{
// declare variables for Invoice object
private int quantityValue;
private decimal priceValue;
// auto-implemented property PartNumber
public int PartNumber { get; set; }
// auto-implemented property PartDescription
public string PartDescription { get; set; }
// four-argument constructor
public Invoice(int part, string description,
int count, decimal pricePerItem)
{
PartNumber = part;
PartDescription = description;
Quantity = count;
Price = pricePerItem;
}
// property for quantityValue; ensures value is positive
public int Quantity
{
get
{
return quantityValue;
}
set
{
if (value > 0) // determine whether quantity is positive
{
quantityValue = value; // valid quantity assigned
}
}
}
// property for pricePerItemValue; ensures value is positive
public decimal Price
{
get
{
return priceValue;
}
set
{
if (value >= 0M) // determine whether price is non-negative
{
priceValue = value; // valid price assigned
}
}
}
// return string containing the fields in the Invoice in a nice format;
// left justify each field, and give large enough spaces so
// all the columns line up
public override string ToString() =>
$"{PartNumber,-5} {PartDescription,-20} {Quantity,-5} {Price,6:C}";
}

You could always just add a getter to your Invoice class
public decimal TotalPrice
{
get
{
return Price * Quantity;
}
}
tools[0].TotalPrice to get the first element in your array

You already "select different objects" here:
foreach (var tool in tools)
{
Console.WriteLine(tool);
}
You could add to this, and even keep a total:
double total = 0;
foreach (var tool in tools)
{
Console.WriteLine($"{tool.Quantity} of {tool.Name} (costing {tool.Price} each) totals {tool.Quantity * tool.Price}");
total += (tool.Quatity * tool.Price);
}
Console.WriteLine("Invoice total " + total);
You can also access arrays etc by index, typical examples being:
for(int x = 0; x < tools.Length; x++)
Console.WriteLine(tools[x].Price);
for(int x = tools.Length-1; x >= 0; x--)
Console.WriteLine(tools[x].Price);
This is a common requirement to do that they have a shortcut - you type forTABTABorforr`TABTAB VS will insert skeletons of these for you

Related

Data validation with mostly feature in List<T> using .NET Core C#

I have fetched the data as List < T > by reading from different formats e.g. CSV, Parquet, Avro, JSON.
I want to validate the data with mostly feature
e.g. The temperature should remain with in range 95% of the time, and rest of the time column value can be null or out of range.
Sample use case expectation:
Expect_Column_Values_To_Be_Between(
columnName = "temprature",
minValue = 60,
maxValue = 75,
mostly = .95
)
Data Annotation seems to solve it partially (missing mostly feature) as it works on row level not on entire table i.e. object level.
[Range(60, 75, ErrorMessage = "Thermostat value {0} must be between {1} and {2}.")]
public int Temprature;
The Python package reference: https://github.com/great-expectations/.great_expectations contains similar data level validations.
Now trying to seek out guidance how to validate the data (either by any existing equivalent library in .NET or by creating new helper class/extension methods)
Created a sample extension method which validate the data at table i.e. object level
public class Room
{
public int RoomId { get; set; }
public string Name { get; set; }
public double Temprature { get; set; }
}
List<Room> rooms = new List<Room>();
rooms.Add(new Room() { RoomId = 1, Name = "Hall", Temprature = 65 });
rooms.Add(new Room() { RoomId = 2, Name = "Kitchen", Temprature = 75 });
bool result = rooms.Expect_Column_Values_To_Be_Between("Temprature", 60, 75, .95);
public static class ValidationExtensions
{
public static bool Expect_Column_Values_To_Be_Between<T>(this List<T> items,
string columnName, double minValue, double maxValue, double mostly = 1)
{
if (mostly < 0 || mostly > 1)
throw new ArgumentOutOfRangeException(
$"Mostly value {{{mostly}}} can not be less 0 or greater than 1");
else if (mostly == 0)
return true;
if (items == null || items.Count == 0)
return false;
int itemsInRangeCount = 0;
foreach (var item in items)
{
PropertyInfo? propertyInfo = item.GetType().GetProperty(columnName);
if (propertyInfo == null)
throw new InvalidDataException($"Column not found : {columnName}");
var itemValue = Convert.ToDouble(propertyInfo.GetValue(item));
if (itemValue >= minValue && itemValue <= maxValue)
itemsInRangeCount++;
}
return (itemsInRangeCount / items.Count) >= mostly ? true : false;
}
}

How to write a CompareTo method to compare string values in binary search algorithm

EDITED: (editd the binary search method)
I am using c sharp. I have a sorted list that has some values in it such as name and their quantity.
My code:
using System;
using System.Collections.Generic;
using static System.Console;
namespace InventoryManagementSystem
{
public class Tool
{
public Tool(string name, int quantity)
{
this.Name = name;
this.Quantity = quantity;
}
public string Name { get; }
public int Quantity { get; }
public int CompareTo(SortedList<string, Tool> other, String name)
{
return; //not sure how to write this properly
}
}
}
//toolitems class
public class ToolItems
{
SortedList<string, Tool> gardeningTools = new SortedList<string, Tool>();
public void gardeningToolData()
{
Tool gardeningTool1 = new Tool("Garden gloves", 50);
gardeningTools.Add(gardeningTool1.Name, gardeningTool1);
WriteLine("Please enter a name of the tool");
String user = ReadLine();
int number = binarySearch(user);
}
public int binarySearch(String user)
{
while (low <= high)
{
mid = (high + low) / 2;
if (gardeningTools.Values[high].Name.CompareTo(user) == 0)
{
WriteLine("mid is: " + mid);
return mid;
}
else if (gardeningTools.Values[high].Name.CompareTo(user) > 0)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
Edited: My code works now except it returns the index 0 if the value is between first and last index of list. How can I get the exact index from the sortedlist depending on what name user search for?
In short all I am trying to do is when user enter some string value and if it matches in the sortedlist (which we search using binary search algorithm) we get the index of that value from sorted list and update the quantity of that specific value (If user chose 'A' and it is at index 0 ("A", 5) then we ask user for how many A you want to borrow and user say 2, so the quantity gets update to 3 now.
SortedList is implementing IDictionary. Basically, your sorted list will have methods that IDictionary has. You can check this link Microsoft Docs SortedList out.
In your question, my understanding is you want to find your Tool object from SortedList and update its quantity. I am not directly answering your question about how to write CompareTo method but instead proposing alternative solution to how you can achieve your actual goal. Please find below sample code:
Slight change on your Tool class, added setter to Quantity property and removed your CompareTo method:
public class Tool
{
public Tool(string name, int quantity)
{
this.Name = name;
this.Quantity = quantity;
}
public string Name { get; }
public int Quantity { get; set; }
}
And here your console app part how to find and update quantity with user inputs:
SortedList<string, Tool> gardeningTools = new SortedList<string, Tool>();
public void gardeningToolData()
{
var gardeningTool = new Tool("Garden gloves", 50);
gardeningTools.Add(gardeningTool.Name, gardeningTool);
Console.WriteLine("Please enter a name of the tool");
var toolName = Console.ReadLine();
Console.WriteLine("Please enter quantity of tools");
var quantity = int.Parse(Console.ReadLine()); // Ideally you should check whether user enters valid integer
if(gardeningTools.TryGetValue(toolName, out var tool))
{
// You should check here whether you have sufficient quantity before decreasing quantity
tool.Quantity -= quantity;
}
}
UPDATE
I've modified this so that I'm also answering how to implement compareto.
public class Tool : IComparable<Tool>, IComparable<string>
{
public Tool(string name, int quantity)
{
this.Name = name;
this.Quantity = quantity;
}
public string Name { get; }
public int Quantity { get; }
public int CompareTo(Tool other)
{
// You could manually compare rather than this
// return this.Name == other?.Name ? 0 : this.Name < other?.Name ? -1 : 1;
return string.Compare(this.Name, other?.Name);
}
public int CompareTo(string other) {
return string.Compare(this.Name, other);
}
}
public int binarySearch(String user)
{
while (low <= high)
{
mid = (high + low) / 2;
switch(gardeningTools.Values[mid].CompareTo(user)) {
case 0: // Found
WriteLine("mid is: " + mid);
return mid;
case -1: // Mid is too small.
low = mid + 1;
break;
default: // Mid is too high
high = mid - 1;
break;
}
// probably should also check that high and low != at this point
return -1;
}

How to assign a value to a field in different class?

In this program, I have 2 classes Application and Customer. I want to initialize Standard Fare with some value which should be entered by the user. Standard Fare field is in Customer class.
I did this, but it is not showing the desired result. When Calculate function is called The Value of Standard fare is becoming zero.
When I initialize the value of STANDARD_FARE in Customer class itself, then the program is working as desired.
How can I input the value given by the user to STANDARD_FARE?
Also methods like GetAge(), GetPassNo() in Application class is not returning the value of the same.
class Application
{
private static int Nop ;
private static double TotalFare=0;
Customer cust= new Customer();
static void Main(string[] args)
{
Application obj = new Application();
Console.Write("Enter the STANDARD RATE of the tour ");
obj.cust.StandardFare = int.Parse(Console.ReadLine());
a:
Console.Clear();
Console.WriteLine("Enter the number of passengers");
Nop = int.Parse(Console.ReadLine());
Application[] app = new Application[Nop];
if (Nop <= 0)
{
Console.WriteLine("Please enter a valid number of passengers");
Console.ReadKey();
goto a;
}
for (int i = 0; i < Nop; i++)
{
app[i] = new Application();
app[i].GetInformationFromCust();
}
for (int j = 0; j < Nop; j++)
{
app[j].cust.Display();
}
}
public int GetInformationFromCust()
{
b:
Console.Clear();
int slen = 0;
Console.WriteLine("Enter the title of the passenger");
cust.Customer_Title = Console.ReadLine();
Console.WriteLine("\r\nEnter passenger's First name :");
cust.Customer_FName = Console.ReadLine();
Console.WriteLine("\r\nEnter passenger's Last name :");
cust.Customer_LName = Console.ReadLine();
slen = cust.Customer_FName.Length + cust.Customer_LName.Length;
if (slen < 5 || slen > 15)
{
Console.WriteLine("\r\nName should be between 5 to 15 characters, Please try again ");
Console.ReadLine();
goto b;
}
c:
long x = 0, len = 0;
Console.WriteLine("\r\nEnter the passport number of the passenger ");
cust.CustomerPassNo = int.Parse(Console.ReadLine());
x = cust.CustomerPassNo;
while (x > 0)
{
x = x / 10;
++len;
}
if (len != 8)
{
Console.WriteLine("\r\nInvalid passport number, passport should be of 8 digits ");
goto c;
}
d:
Console.WriteLine("\r\nEnter the age of the passenger :");
cust.Customer_Age = int.Parse(Console.ReadLine());
if (cust.Customer_Age < 0)
{
Console.WriteLine("\r\nInvalid age, please enter a valid age ");
goto d;
}
cust.CalculatePrice();
return 0;
}
public int GetAge()
{
return cust.Customer_Age;
}
public double GetAirFare()
{
return cust.CustomerTicket ;
}
public long GetPassportNo()
{
return cust.CustomerPassNo;
}
public string GetTitle()
{
return cust.Customer_Title;
}
}
class Customer
{
const double K_DISCOUNT = 0.10;
const double S_DISCOUNT = 0.20;
private double STANDARD_FARE;
private string CustomerName { get; set; }
private int CustomerAge;
private string CustomerFName;
private string CustomerLName;
private long CustomerPassport;
private double CustomerPrice;
private string CustomerTitle;
private double KidDiscount;
private double SeniorDiscount;
public Customer()
{
this.KidDiscount = K_DISCOUNT;
this.SeniorDiscount = S_DISCOUNT;
}
public double StandardFare
{
get { return STANDARD_FARE; }
set { STANDARD_FARE = value; }
}
public int Customer_Age
{
get { return CustomerAge; }
set { CustomerAge = value; }
}
public string Customer_Title
{
get { return CustomerTitle; }
set { CustomerTitle = value; }
}
public string Customer_FName
{
get { return CustomerFName; }
set { CustomerFName = value; }
}
public string Customer_LName
{
get { return CustomerLName; }
set { CustomerLName = value; }
}
public long CustomerPassNo
{
get { return CustomerPassport; }
set { CustomerPassport = value; }
}
public double CustomerTicket
{
get { return CustomerPrice; }
set { CustomerPrice = value; }
}
public int CalculatePrice()
{
if (CustomerAge < 3)
{
CustomerPrice = 0;
}
else if (CustomerAge >= 3 && CustomerAge < 18)
{
CustomerPrice = STANDARD_FARE - (STANDARD_FARE * KidDiscount);
}
else if (CustomerAge > 65)
{
CustomerPrice = STANDARD_FARE - (STANDARD_FARE * SeniorDiscount);
}
else
{
CustomerPrice = STANDARD_FARE;
}
return 0;
}
public void Display()
{
//some code here
}
}
You are populating your array app with instances of Application that still have the default STANDARD_FARE value (which is 0.0), because you have never set it on those instances. You only set it on the obj.cust instance, which you never again use. Because STANDARD_FARE is an instance variable, changes to it have no affect on other (or future) instances.
You have the same problem in reverse with all the Application.Get* functions; they are getting properties of an object (obj.cust) that has never had any properties set, other than StandardFare/STANDARD_FARE.
The most obvious fix is to do away with obj and obj.cust entirely - they have no use other than to be confusing - and make STANDARD_FARE a static variable (and its setter StandardFare a static property).
BTW, your naming conventions are terrible and inconsistent; if I were your grader I'd dock you points for using unclear variable names(app, nop), and for using ALL_CAPS for non-constants (STANDARD_FARE). I'd also object to using a private auto-backed property (CustomerName, which is also never used) instead of simply a private variable, for not using auto-backed properties elsewhere (StandardFare as an explicitly-coded public getter and setter for STANDARD_FARE, etc.), and for copying constant values into non-settable instance variables (K_DISCOUNT to KidDiscount; just use the constant directly, or at least make KidDiscount static and add some non-private access to it). As others have mentioned, you of course should not be using goto in place of loops. I'll also mention the error-prone and inefficient checking the length of the passport number by repeated division instead of simply checking whether it's less than 99999999 (in theory, passport numbers might start with a zero, which would look like less than 8 digits after parsing, but you could also make sure it's greater than 10000000 if you want).

How to get one result from a list in another method?

Hi all I'm new to C#.
I try to return a result "totalAmount" from my method called "GetAllKschl". In this method I returned a list with "KSCHL, KSCHLData, price, pieces und totalPrice".
So in my new method I need the total amount of all "totalPrice" together.
first method:
public List<Result> GetAllKschl(string fileNameResult, string fileNameData)
{
List<Result> listResult = new List<Result>();
docResult.Load(fileNameResult);
docData.Load(fileNameData);
var resultList = docResult.SelectNodes("//root/CalculationLogCompact/CalculationLogRowCompact");
foreach (XmlNode nextText in resultList)
{
XmlNode KSCHL = nextText.SelectSingleNode("KSCHL");
string nextKschl = KSCHL.InnerText;
// ... and so on...
if (pieces > 0 && totalPrice > 0)
{
listResult.Add(new Result(nextKschl, nextKSCHLData, nextEinzelpreis, pieces, totalPrice));
}
}
return listResult;
}
second method: (don't know exactly what to do)
public decimal GetTotalAmount(string amount, string totalAmount)
{
string total = GetAllKschl(amount, totalAmount); // ??
return total;
}
So here I want to have just the TotalAmount (every totalPrice from GetAllKschl) und not the whole list from GetAllKschl. How do I do this?
here my class result:
public class Result
{
public string KSCHL { get; set; }
public string Info { get; set; }
public int individualPrice { get; set; }
public int Pieces { get; set; }
public int TotalCosts { get; set; }
public Result(string kschl, string info, int individualPrice, int pieces, int totalCosts)
{
KSCHL = kschl;
Info = info;
IndividualPrice = individualPrice;
Pieces = pieces;
TotalCosts = totalCosts;
}
}
You can use LINQ extension method Sum to do so:
decimal total = GetAllKschl( amount, totalAmount ).Sum( result => result.Gesamtpreis );
I assume that the TotalPrice is the name of the property for price in the Result class.
The Sum extension method iterates over all items in the returned collection and sums up the prices.
You could rewrite this without LINQ like this:
var list = GetAllKschl( amount, totalAmount );
decimal total = 0;
foreach ( var item in list )
{
total += item.Gesamtpreis;
}
As a suggestion, I would recommend making clearer variable naming conventions and do not mix variable names from different languages (English and German).
Also it is quite unusual you used decimal for the total price while Result class uses int. Maybe result should have decimals as well? It seems fitting for a price property.
The best would be probably using LINQ:
public decimal GetTotalAmount(string amount, string totalAmount)
{
var total = GetAllKschl(amount, totalAmount).Sum(result => result.Gesamtpreis);
return total;
}
I'm assuming that Your result are in the property called Gesamtpreis and is of any numeric type.
EDIT:
Based on the comments I decided to put there a bit more description about LINQ extension methods and lambda method. LINQ methods allows You to use a query language similar to SQL. It works with Collection of elements (e.g. List of Result - List<Result>). On this collection You will call these methods and they will provide You some kind of result, sometimes just number (Aggregate functions like Min,Max,Sum,..) or they will do some other actions returning object or another collection (First,Last, ToList, ToDictionary).
In our care we will have a List with objects:
public class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
List<Product> productList = new List<Product>();
productList.Add(new Product() { Name = "Car", Price = 140000 });
productList.Add(new Product() { Name = "SSD Disc", Price = 2000 });
productList.Add(new Product() { Name = "Bananan", Price = 7 });
Having those, for normal SUM You would go with:
int result = 0;
foreach(var nProduct in productList)
result += nProduct.Price;
Console.WriteLine(result);
This is kind of short code, but it can be pretty much simplified without using variable (for intermediate results) and foreach cycle. (Actually the foreach cycle will be used but we won't need to handle/write it.) LINQ example:
var result = productList.Sum(nProduct => nProduct.Price);
Now this code is much shorter, but we have to split it into several parts to understand what actually happened:
// Saving result to variable (as anytime before)
// Note that I have changed "int" to "var", which simplifies the code,
// as You don't have to take care of "what type will the result be"
// usage is really common with LINQ also
var result =
// calling method Sum() on the productList
productList.Sum()
// Sum will take each object in the collection and put it as a parameter called "nProduct"
// now the "=>" is something called Lambda syntax,
// that allows take paremeters from the left side and use them in code on the right side.
// left side is instance of "Product" class named as "nProduct"
Sum(nProduct => ... )
// ... is replaced with "nProduct.Price",
// which is selector that tells "make the sum of property "Price"
Sum(nProduct => nProduct.Price)
// In similar manner works other aggregate functions
var max = productList.Max(prod => prod.Price);
var min = productList.Min(prod => prod.Price);
var avg = productList.Average(prod => prod.Price);
//In this method you are returning a List
public List<Result> GetAllKschl(string fileNameResult, string fileNameData)
{
List<Result> listResult = new List<Result>();
docResult.Load(fileNameResult);
docData.Load(fileNameData);
var resultList = docResult.SelectNodes("//root/CalculationLogCompact/CalculationLogRowCompact");
foreach (XmlNode nextText in resultList)
{
XmlNode KSCHL = nextText.SelectSingleNode("KSCHL");
string nextKschl = KSCHL.InnerText;
// ... and so on...
if (pieces > 0 && totalPrice > 0)
{
listResult.Add(new Result(nextKschl, nextKSCHLData, nextEinzelpreis, pieces, totalPrice));
}
}
return listResult;
}
//On the second method you returning a decimal and expecting a string
public decimal GetTotalAmount(string amount, string totalAmount)
{
string total = GetAllKschl(amount, totalAmount); // ??
return total;
}
It would be best to change the second method as:
decimal total = GetAllKschl(amount, totalAmount).Sum(result => result.Gesamtpreis);
Add linq in the return.

How to iterate an array of objects, accumulate a property and update another property(a Total) of each item without using two for statement?

Having a model something like this (I cannot change this):
public class SomeObject
{
public int Amount { get; set; }
public int TotalAmount { get; set; }
}
I need to iterate an array of SomeObject to populate some values and accumulate (perform not simple calculations) another fields.
static void Main(string[] args)
{
List<SomeObject> myCollection = new List<SomeObject>()
{
new SomeObject() { Amount = 3 },
new SomeObject() { Amount = 6 },
new SomeObject() { Amount = 9 }
};
int totalAccumulated = 0;
for (int i = 0; i < myCollection.Count; i++)
{
PopulateAndCalculate(myCollection[i], ref totalAccumulated);
}
//I don't want to create here a second for to iterate again all myCollection to set his TotalAmount property.
//There is another way?
Console.WriteLine($"The total accumulated is: {totalAccumulated}");
}
private static void PopulateAndCalculate(SomeObject prmObject, ref int accumulatedTotal)
{
//Populate a lot of another fields
accumulatedTotal += prmObject.Amount;
prmObject.TotalAmount = accumulatedTotal; //This don't work, but I need something alike
}
I don't want a second for statement to update TotalAmount property of each item in myCollection.
The main requirement is iterate the whole array, few times, don't care about string interpolation this is a short demo, this code must run in .net 2.0.
Theres is a clean/better way?
The solution is actually simple, though it's not exactly a good coding practice.
What you really need is for TotalAmount to be a static property. Without that, there's this:
static void Main(string[] args)
{
List<SomeObject> myCollection = new List<SomeObject>()
{
new SomeObject() { Amount = 3 },
new SomeObject() { Amount = 6 },
new SomeObject() { Amount = 9 }
};
int totalAccumulated = 0;
for (int i = 0; i < myCollection.Count; i++)
{
PopulateAndCalculate(myCollection[i], ref totalAccumulated);
}
/*****This is the new part*******/
myCollection[0].TotalAmount = totalAccumulated;
myCollection[1].TotalAmount = totalAccumulated;
myCollection[2].TotalAmount = totalAccumulated;
Console.WriteLine($"The total accumulated is: {totalAccumulated}");
}
private static void PopulateAndCalculate(SomeObject prmObject, ref int accumulatedTotal)
{
//Populate a lot of another fields
accumulatedTotal += prmObject.Amount;
//no need to mess with the total here as far as the properties are concerned.
}
You can st fields inside linq expression.
Could you consider this please
myCollection.ForEach(c => c.TotalAmount = myCollection.Sum(a => a.Amount));
Console.WriteLine($"Total accumulated :{myCollection.First().TotalAmount}");
I found a solution using the Observer Pattern.
Firstly I created a global delegate to be used by an event:
public delegate void UpdateTotalAmountDelegate(int totalAmount);
Then a new class called: 'CalculatorSetter'
public class CalculatorSetter
{
public event UpdateTotalAmountDelegate UpdateTotalAmounthHandler;
public void UpdateTotalAmount(int prmTotalAmount)
{
UpdateTotalAmounthHandler(prmTotalAmount);
}
}
I refactor the data object 'SomeObject' adding a field of type CalculatorSetter.
public class SomeObject
{
private CalculatorSetter finalCalculator;
public void SetCalculator(CalculatorSetter prmCalculator)
{
this.finalCalculator = prmCalculator;
finalCalculator.UpdateTotalAmounthHandler += FinalCalculator_UpdateTotalAmounthHandler;
}
private void FinalCalculator_UpdateTotalAmounthHandler(int totalAmount)
{
this.TotalAmount = totalAmount;
}
//Some Other Fields
public int Amount { get; set; }
public int TotalAmount { get; set; }
}
And my original code and unique for:
static void Main(string[] args)
{
List<SomeObject> myCollection = new List<SomeObject>()
{
new SomeObject() { Amount = 3 },
new SomeObject() { Amount = 6 },
new SomeObject() { Amount = 9 }
};
CalculatorSetter commonCalculator = new CalculatorSetter();
int totalToAccumulate = 0;
for (int i = 0; i < myCollection.Count; i++)
{
PopulateAndCalculate(myCollection[i], commonCalculator, ref totalToAccumulate);
}
commonCalculator.UpdateTotalAmount(totalToAccumulate);
Console.WriteLine($"The total accumulated is: {totalToAccumulate}");
Console.WriteLine($"The first total accumulated is: {myCollection[0].TotalAmount}");
}
Many thanks.
Use a wrapper and keep it simple (if you want you can change a little for use static methods you can, or static class but I dont see the point)
the result is:
The Amount is 3, The total ammount is 18
The Amount is 6, The total ammount is 18
The Amount is 9, The total ammount is 18
namespace Prueba1
{
class Program
{
public class WrapperInt {
public int Value { get; set; }
}
public class SomeObject
{
public int Amount { get; set; }
public WrapperInt TotalAmount { get; set; }
}
public Program() {
WrapperInt TotalAmountAllArrays = new WrapperInt();
List<SomeObject> myCollection = new List<SomeObject>()
{
new SomeObject() { Amount = 3, TotalAmount =TotalAmountAllArrays },
new SomeObject() { Amount = 6 , TotalAmount =TotalAmountAllArrays },
new SomeObject() { Amount = 9 , TotalAmount =TotalAmountAllArrays }
};
for (int i = 0; i < myCollection.Count; i++)
{
myCollection[i].TotalAmount.Value += myCollection[i].Amount;
}
foreach (var c in myCollection)
{
Console.WriteLine($"The Amount is:" + c.Amount + " The total ammount is:" + c.TotalAmount.Value);
}
}
static void Main(string[] args)
{
new Program();
}
}
}
Hopefully this will work for you… One possible solution is to create a wrapper class called MyTotalList which contains a List named amounts and an int named total. MyTotalList class does not expose its list amounts as an editable list. If the class exposes this list as editable, then other methods could ultimately change an items value in that list and the MyTotalList class would not be aware of this and unfortunately contain an incorrect total. To avoid this situation and for the class to work as expected, methods must use the MyTotalList’s Add and Remove methods. To ensure this happens, the private List amounts in the MyTotalList class returns a read only list which ensures that changes to the list will not be made outside the MyTotalList class. Leaving the list exposed and editable will/could cause the class to contain an incorrect total.
My solution is to create a Class that wraps a List. MyTotalList class has a no argument constructor. Once a new instance of a MyTotalList object is created you can then use that instance to Add MyObject items to its list. Every time an item is added to the MyTotalList, list amounts the variable total gets updated with the added item’s amount. Example:
Create a new MyTotalList object:
MyTotalList listOfObjects = new MyTotalList();
Then add some MyObject instances to the listOfObjects
listOfObjects.Add(new MyObject(1,3));
listOfObjects.Add(new MyObject(2,6));
listOfObjects.Add(new MyObject(3,9));
After you add the items, you can then use the listOfObjects Total property to get the total sum of all MyObject items in the list with:
listOfObjects.Total
If you need to pass or use the List of MyTotalList items you can use:
listOfObjects.Items
Bear in mind as I discussed above, this List Items is a read-only list. Therefore you cannot add/remove items in this list as you would an editable list. So the code below will fail during implementation as these methods are not exposed for read only objects.
listOfObjects.Items.Remove(new MyObject(4, 10));
listOfObjects.Items.Add(new MyObject(4, 10));
The above lines will cause the compiler to complain: xxx… does not contain a definition for Add/Remove. This ensures methods will use the MyTotalList.Add and MyTotalsList.Remove methods and eliminate any possibility of the list changing outside the MyTotalList class.
MyObject Class
class MyObject : IComparable {
public int id { get; }
public int amount { get; }
public MyObject(int inID, int inAmount) {
id = inID;
amount = inAmount;
}
public override string ToString() {
return amount.ToString();
}
public override int GetHashCode() {
return id.GetHashCode();
}
public override bool Equals(object other) {
if (other != null)
return (this.id == ((MyObject)other).id);
return false;
}
public int CompareTo(object other) {
if (this.id > ((MyObject)other).id)
return 1;
if (this.id < ((MyObject)other).id)
return -1;
return 0;
}
}
MyTotalList Class
class MyTotalList {
private int total;
private List<MyObject> amounts;
public MyTotalList() {
total = 0;
amounts = new List<MyObject>();
}
public int ListCount {
get { return amounts.Count; }
}
public IReadOnlyCollection<MyObject> Items {
get { return amounts.AsReadOnly(); }
}
public int Total {
get { return total; }
}
public void Add(MyObject other) {
if (other != null) {
if (!(amounts.Contains(other))) {
total += other.amount;
amounts.Add(other);
}
else {
Console.WriteLine("Duplicate id's not allowed!");
}
}
}
public void Remove(MyObject other) {
if (amounts.Contains(other)) {
total -= amounts[amounts.IndexOf(other)].amount;
amounts.Remove(other);
}
else {
Console.WriteLine("Item to remove not found!");
}
}
}
Examples
MyTotalList listOfObjects = new MyTotalList();
listOfObjects.Add(new MyObject(1,3));
listOfObjects.Add(new MyObject(2,6));
listOfObjects.Add(new MyObject(3,9));
Console.WriteLine("----------------------------------------");
Console.WriteLine("Initial list with total");
Console.WriteLine("items in list:");
foreach (MyObject mo in listOfObjects.Items)
Console.Write(mo.ToString() + " ");
Console.WriteLine();
Console.WriteLine("Total from list of " + listOfObjects.ListCount +
" items is: " + listOfObjects.Total);
Console.WriteLine("----------------------------------------");
Console.WriteLine("Add three more items");
listOfObjects.Add(new MyObject(4, 10));
listOfObjects.Add(new MyObject(5, 11));
listOfObjects.Add(new MyObject(6, 12));
Console.WriteLine("items in list:");
foreach (MyObject mo in listOfObjects.Items)
Console.Write(mo.ToString() + " ");
Console.WriteLine();
Console.WriteLine("Total from list of " + listOfObjects.ListCount +
" items is: " + listOfObjects.Total);
Console.WriteLine("----------------------------------------");
Console.WriteLine("Remove id 4 (10) from the list");
listOfObjects.Remove(new MyObject(4, 10));
Console.WriteLine("items in list:");
foreach (MyObject mo in listOfObjects.Items)
Console.Write(mo.ToString() + " ");
Console.WriteLine();
Console.WriteLine("Total from list of " + listOfObjects.ListCount +
" items is: " + listOfObjects.Total);
A Side note to your original post…About the class you can not change
SomeObject {
public int Amount { get; set; }
public int TotalAmount { get; set; }
}
Regardless of how you get the total for theint varable: TotaAmount… for each instance of SomeObject class to contain the same variable with the same amount and you want to ensure this is true for all existing SomeObject instances… is well a poor design. This creates redundant data and simply waste space and it makes no sense for each variable to contain this value as it has absolutely nothing to do with that SomeObject instance. This class design is counter intuitive of a good design. As #Tim Schmelter’s comment points out "a single object should not know anything about the total amount of other objects." This “redundant data” situation is something a programmer should try to avoid, not promote.

Categories

Resources