I have two rules I want to implement in the XML application.
1 If weight is bigger then 10 then by name Department has to be
written: Big
It is best to let the Department property itself compute its value (computed property). This is cleaner and more readable as the computation takes place where you would expect it. Otherwise in a scenarion where the computation depends on more than a single property you would find yourself writing duplicate computational code in each participating property.
Therefore, execute the computation when the value is requested - which is when the computed property's Get() is called - and not when the partzicipating properties have changed. If the computation is more complex, then following this rules will improve performance too.
Also avoid implementing such a filter directly in the Set()/Get() of a property. Move it to a method.
I also suggest using a switch-statement or switch-expression as this is more readable than a long chain of if-else blocks and therefore easier to maintain. When using C# 9, then switch-expression can be a powerful tool to filter with a nicely readable syntax.
Furthermore, since the string you are trying to create for thze print out is a fixed string representation of the actual Parcel instance, it would be more appropriate to override the Parcel.ToString.
Your Parcel class should look like something this:
Parcel.cs
public class Parcel
{
public override string ToString()
=> $"Name: {this.Name} - Postal code {this.PostalCode} - Weight {this.Weight} - Value {this.Value} - Department {this.Department}";
public string Name { get; set; }
public string PostalCode { get; set; }
public decimal Weight { get; set; }
public decimal Value { get; set; }
public string Department => CreateDepartmentValue();
private string CreateDepartmentValue()
{
var result = string.Empty;
switch (this.Weight)
{
case decimal weightInKiloGrams when weightInKiloGrams <= 1: result = "Mail"; break;
case decimal weightInKiloGrams when weightInKiloGrams <= 10: result = "Regular"; break;
case decimal weightInKiloGrams when weightInKiloGrams > 10: result = "Heavy"; break;
};
switch (this.Value)
{
case decimal value when value > 1000: result += ", Insurance"; break;
};
return result;
}
}
Usage Example
public class Program
{
static void Main(string[] args)
{
XDocument xdoc = XDocument.Load($"Container.xml");
var items = ...;
foreach (var item in items)
{
// Call ToString() implicitly
Console.WriteLine(item);
Console.WriteLine("*********************************************************************");
}
Console.ReadLine();
}
}
You can Create a class Parcel and determine the Department based on Weight & value from function call
public class Parcel
{
public string Name { get; set; }
public string PostalCode { get; set; }
public decimal Weight { get; set; }
public decimal Value { get; set; }
public string Department => GetDepartment();
private string GetDepartment()
{
string _department = "";
if (this.Weight <= 1)
{
_department = "Mail";
}
else if (this.Weight > 1 && this.Weight <= 10)
{
_department = "Regular";
}
else if (this.Weight > 10)
{
_department = "Heavy";
}
else
{
_department = "Unknown";
}
if (this.Value > 1000)
{
_department += ",Insurance";
}
return _department;
}
}
Your XDcoument will look like below
XDocument xdoc = XDocument.Load($"XMLFile1.xml");
var items = xdoc.Descendants("Parcel")
.Select(xelem => new Parcel
{
Name = xelem.Element("Sender").Element("Name").Value,
PostalCode = xelem.Element("Sender").Element("Address").Element("PostalCode").Value,
Weight = Convert.ToDecimal(xelem.Element("Weight").Value),
Value = Convert.ToDecimal(xelem.Element("Value").Value)
});
foreach (var item in items)
{
Console.WriteLine($"{ item.Name} - { item.PostalCode} - { item.Weight} - { item.Value} - { item.Department}");
}
Output
Klaas - 2402AE - 0.02 - 0.0 - Mail
ykken groot B.V. - 2497GA - 2.0 - 0.0 - Regular
seti - 2497GA - 100.0 - 2000.0 - Heavy,Insurance
Aad - 2353HS - 11 - 500 - Heavy
Why not extend your Parcel class to have a 'DepartmentSize' property, which handles the size:
class Parcel
{
public string DepartmentSize
{
var parts = new List<string>();
if(_Weight> 10)
parts.Append("Big");
if(_Value > 1000)
parts.Append("Large");
return string.Join(",", parts);
}
}
Then you can output the size like this:
Console.WriteLine($"... Department: {item.DepartmentSize}");
Related
There is a class for passengers which contain string name, int age, string job etc. I made an array of this class, lets say it has 10 places.
I wanna find the oldest passenger.
My code doesn't work, because it is not possible to compare passenger[i] with an integer. I mean I need only the age in passenger[]
How to find the oldest one in passenger[]?
EDIT: The return value should be a passenger by his name and major, not only its age.
public Passenger Oldest()
{
int oldest = 0;
for (int i = 0; i < passengers.Length; i++)
{
if (passengers[i] > oldest)
{
oldest = passengers[i];
}
}
return oldest;
}
class Passenger
{
int age;
string name;
string major;
public Passenger(int _age, string _name, string _major)
{
age = _age;
name = _name;
major = _major;
}
}
Firstly, as mentioned by #Cid in comment to question all fields of Passenger are private (by default when modifier is not specified). You should either mark then as public (to access them outside declaring class) or better create public properties:
class Passenger
{
public int Age { get; set; } // public auto property
public string Name { get; set; } // public auto property
public string Major { get; set; } // public auto property
public Passenger(int age, string name, string major)
{
Age = age;
Name = name;
Major = major;
}
}
Secondly, you need to compare Age (proeprty, not whole object) of the passenger:
if (passengers[i].Age > oldest)
{
oldest = passengers[i].Age;
}
Also, you could use LINQ to find the oldest passenger:
var oldest = passengers.Max(item => item.Age);
Finally, to return the oldest passenger:
public Passenger Oldest()
{
// if no passengers -> return null
if (!passengers?.Any() ?? true)
{
return null;
}
var maxAge = passengers.Max(item => item.Age);
return passengers.First(item => item.Age == maxAge);
}
Also, as mentioned by #DmitryBychenko the method can be shortened to:
public Passenger Oldest()
{
// if no passengers -> return null
if (!passengers?.Any() ?? true)
{
return null;
}
return passengers.Aggregate((s, a) => s.Age > a.Age ? s : a);
}
or without LINQ:
public Passenger Oldest()
{
// if no passengers -> return null
if (passengers == null || passengers.Length == 0)
{
return null;
}
var maxAge = passengers[0].Age;
var oldestPassengerIndex = 0;
for (var i = 1; i < passengers.Length; i++)
{
if (passengers[i].Age > maxAge)
{
oldest = passengers[i].Age;
oldestPassengerIndex = i;
}
}
return passengers[oldestPassengerIndex];
}
Slightly more efficient version of Roman's answer, especially if the oldest passenger appears last in the list:
public Passenger Oldest()
{
if (passengers.Length == 1) return passengers[0];
var oldest = passengers[0];
for (int i = 1; i < passengers.Length; i++)
{
if (passengers[i].Age > oldest.Age) oldest = passengers[i];
}
return oldest;
}
This only ever requires a single iteration.
Using Linq is easy
var oldest=passengers.Max(x=>x.Age):
Otherwise
Passenger oldest=new Passenger(0,"","");
foreach (Passenger p in Passengers){
if (p.age>oldest.age) oldest=p;
}
I have a class Person which contains several properties. Multiple instances of this class are in an array PersonList.
Is it possible to compare properties of different instances of an object in an array?
For example : find out who is the oldest and who is the youngest ?
public class Persoon
{
public Persoon()
{
}
//Properties maken
public Persoon(string naam, Int16 gewicht, Int16 lengte, DateTime geboortedatum)
{
this.Naam = naam;
this.Gewicht = gewicht;
this.Lengte = lengte;
this.Geboortedatum = geboortedatum;
}
public string Naam { get; set; } // AutoProperty
public double Gewicht { get; set; } // AutoProperty
public int Lengte { get; set; } // AutoProperty
public DateTime Geboortedatum { get; set; } // AutoProperty
public double BerekenBmi()
{
//BMI formule: Gewicht in kilogram / (Lengte in meter * Lengte in meter)
return Math.Round(Gewicht/((Lengte/100.0) * (Lengte/100.0)),1);
}
public string BmiStadiumBerekenen()
{
if (BerekenBmi() < 18.5) return "ondergewicht";
if (BerekenBmi() >= 18.5 && BerekenBmi() <= 24.9) return "normaal";
if (BerekenBmi() >= 25 && BerekenBmi() <= 29.9) return "overgewicht";
if (BerekenBmi() >= 30 && BerekenBmi() <= 34.9) return "obesitas I";
if (BerekenBmi() >= 35 && BerekenBmi() <= 39.9) return "obesitas II";
else return "morbide obesitas";
}
public int BerekenLeeftijd()
{
TimeSpan leeftijd = DateTime.Today - Geboortedatum;
return (int) leeftijd.TotalDays;
}
}
I need to compare the oldest and the youngest person so i can calculate the amount of time that is between them.. I've done this before with only 2 persons without arrays but now i need to do it with them.
Of course you can, and the solution is in your question! You have to use a compareTo() method.
You have to implement the compareTo() method in the class Person.
public class Person implements Comparable<Person>{...}
then you simply add the method
#Override
public int compareTo(Person p) {...}
This method want another Person object in the parameters, so you can choose the way to compare the people. For example you said that you want to compare the age of each Person, you have just to put an if close sentence inside the compareTo() method.
int result;
if (this.age < p.getAge()) {
esito = -1;
}
else if(this.age = p.getAge()){
esito = 0;
}
else{
esito = 1;
}
return esito;
This method returns an int number: a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
Source:
http://docs.oracle.com/javase/7/docs/api/
School lessons
:)
Possibly something like this (using Linq):
var youngest = personList.OrderBy(p => p.Age).First();
(Noting that this assumes that there is seomthing in your list to start with).
Lets say that I have the class Invoice:
public class Invoice
{
public int PartNumber { get; set; }
public string PartDescription { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
}
and then I have an array of its objects in the variable arrayOfInvoices .
If I had to Group invoices in two groups- invoices with Unit price below 12 and invoices with unit price above or equal to 12 and display details about invoices in each group sorted in ascending order of the price, how could I do that ?
You can simply do something like this:
var results =
from inv in arrayOfInvoices
orderby inv.Price
group inv by inv.Price < 12;
Or if you prefer fluent syntax:
var results = arrayOfInvoices.OrderBy(inv => inv.Price)
.GroupBy(inv => inv.Price < 12);
To group the invoices into three or more 'buckets', you can use BinarySearch:
var priceBoundaries = new[] { 12m, 20m };
var results =
from inv in arrayOfInvoices
orderby inv.Price
let index = Array.BinarySearch(priceBoundaries, inv.Price)
group inv by (index < 0 ? ~index : index + 1);
Or use side effects, like this:
var priceBoundaries = new[] { 12m, 20m, Decimal.MaxValue }; // Note the addition of MaxValue
var i = 0;
var results =
from inv in arrayOfInvoices
orderby inv.Price
group inv by (inv.Price < priceBoundaries[i] ? i : ++i);
This is generally bad practice, but should perform better than the BinarySearch method above.
If using group function is a pain (sometimes it gets annoying), then you can also use "Where"
var invoices = new List<Invoice> ();
var group1= invoices.Where(i=> i.Price<12).Orderby(i=> i.Price).ToList();
var group2= invoices.Where(i=> i.Price>=12).Orderby(i=> i.Price).ToList();
You can encapsulate the concept of the range in a class:
private class PriceRange<T> : IEquatable<PriceRange<T>>
{
public T Min { get; set; }
public T Max { get; set; }
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 31 + Min.GetHashCode();
hash = hash * 31 + Max.GetHashCode();
return hash;
}
}
public override bool Equals(object obj)
{
return Equals(obj as PriceRange<T>);
}
public bool Equals(PriceRange<T> other)
{
if (other == null) return false;
if (!Min.Equals(other.Min)) return false;
if (!Max.Equals(other.Max)) return false;
return true;
}
}
Then use a map function to map the prices of each of your invoices to the appropriate range:
private class PriceRangeFactory<T>
{
public PriceRangeFactory(T[] rangeCutoffs)
{
_RangeCutoffs = rangeCutoffs;
}
private T[] _RangeCutoffs;
public PriceRange<T> Map(T price)
{
var index = Array.BinarySearch(_RangeCutoffs, price);
// Assume that the _RangeCutoffs that we have fully cover all possible values for T.
if (index < 0) index = ~index;
return new PriceRange<T>() { Min = _RangeCutoffs[index - 1], Max = _RangeCutoffs[index] };
}
}
Then use GroupBy with that map function:
var rangeFactory = new PriceRangeFactory<decimal>(new decimal[] { decimal.MinValue, 12, 20, decimal.MaxValue });
var grouped = foos.GroupBy(a => rangeFactory.Map(a.Price));
You get a list of IGroupings each keyed by a range that you specified, with the appropriate objects that fit into that range attached.
Now, obviously the code above is not quite production level, but it should be enough to get you started. As for why its not production level:
There's no check to assert that the ranges supplied to the PriceRangeFactory actually do fully cover all possible values.
It always assumes that the range is described as > X and <= Y.
There's no checking around Range.Min < Range.Max.
There's no assert that the list of range cutoffs supplied to the PriceRangeFactory are ordered correctly (required for BinarySearch).
There are bound to be some edge cases I haven't covered.
I have the following object and I want a dictionary to conditionally determine if there is a duplicate. For example, in one dictionary I only care about two properties being unique for my key. In a second dictionary, I want all the properties being unique for the key.
Question 1:
What interfaces should I override to accomplish this? (e.g. GetHashCode, IEqualityComparer, equals operator)
Question 2:
What should I do if I change a property that ultimately changes the value of the key? This is probably more relevant if I do an Dictionary since .NET framwork somehow handles this for me, but I never thought about it.
Code
public class EventData : IEqualityComparer<EventData>
{
public string ComputerName { get; set; }
public Guid? CategoryName { get; set; }
public string LogName { get; set; }
public int EventID { get; set; }
public long? EventUniqueTracker { get; set; }
public DateTime LastQueryDate { get; set; }
public DateTime? DateOfRecord { get; set; }
//public int QueryCount { get; set; }
public int QueryCount = 0 ;//
public string zData { get; set; }
public EventData(string computerName, Guid? categoryName, string logName, int eventID, long? eventUniqueTracker, int queryCount)
{
ComputerName = computerName;
CategoryName = categoryName;
LogName = logName;
EventID = eventID;
EventUniqueTracker = eventUniqueTracker;
LastQueryDate = DateTime.Now;
QueryCount = queryCount;
}
public EventData()
{
}
public override int GetHashCode()
{
return GetHashCode(HashType.ZCompCatLogEventAllData);
}
public object GetString(HashType hType)
{
switch (hType)
{
case HashType.AComputerName:
return ComputerName;
break;
case HashType.BCompAndCat:
return new { A = ComputerName, B = CategoryName };
break;
case HashType.CCompCatLog:
return new { A = ComputerName, B = CategoryName, C = LogName };
break;
case HashType.DCompCatLogEvent:
return new { A = ComputerName, B = CategoryName, C = LogName, D = EventID };
break;
case HashType.ECompCatLogEventUserDefined1:
case HashType.FCompCatLogEventUserDefined2:
case HashType.ZCompCatLogEventAllData:
return new { A = ComputerName, B = CategoryName, C = LogName, D = EventID, E = EventUniqueTracker };
default:
break;
}
return new object { };
}
public int GetHashCode(HashType hType)
{
return GetString(hType).GetHashCode();
return 1;
}
public override string ToString()
{
return ComputerName + " " + CategoryName + " " + LogName + " " + EventID + " " + EventUniqueTracker;
}
public bool Equals(EventData x, EventData y)
{
return x.ComputerName == y.ComputerName &&
x.CategoryName == y.CategoryName &&
x.LogName == y.LogName &&
x.EventID == y.EventID &&
x.EventUniqueTracker == y.EventUniqueTracker;
}
public int GetHashCode(EventData obj)
{
EventData ci = (EventData)obj;
// http://stackoverflow.com/a/263416/328397
return new { A = ci.ComputerName, B = ci.CategoryName, C = ci.LogName, D = ci.EventID, E = ci.EventUniqueTracker }.GetHashCode();
}
}
It sounds like you should be implementing IEqualityComparer<EventData> - but not within EventData itself. Create two separate implementations - one for the first notion of equality, and one for the second. Then create your dictionaries as:
var first = new Dictionary<EventData, string>(new PartialDataEqualityComparer());
var second = new Dictionary<EventData, string>(new FullDataEqualityComparer());
Or perhaps you want to treat the second case as the "natural" equality for EventData, in which case you could make EventData implement IEquatable<EventData> and not specify a comparer when creating the second dictionary.
Basically, you implement IEquatable<T> to say "an instance of this type is capable of comparing itself against an instance of T" whereas you implement IEqualityComparer<T> to say "an instance of this type is capable of comparing any two instances of T".
What should I do if I change a property that ultimately changes the value of the key?
You're stuffed, basically. You won't (or at least probably won't) be able to find that key again in your dictionary. You should avoid this as carefully as you possibly can. Personally I usually find that classes which are good candidates for dictionary keys are also good candidates for immutability.
I have a class that has a dozen or so properties that represent various financial fields. I have another class that needs to perform some calculations on each of those fields separately. The code inside those calculation methods are identical except for the field that it does the calculation on.
Is there a way that I can pass a property name as a parameter and just have one method that does all of the performing work instead of the 12 methods for each property?
Also, I'm sure this can be accomplished via reflection, but I've seen in other code where lambdas are used in this same kind of fashion and was wondering if this is a candidate where this can be used.
As requested, here is an example:
public class FinancialInfo
{
public virtual DateTime AuditDate { get; set; }
public virtual decimal ReleasedFederalAmount { get; set; }
public virtual decimal ReleasedNonFederalAmount { get; set; }
public virtual decimal ReleasedStateAmount { get; set; }
public virtual decimal ReleasedLocalAmount { get; set; }
public virtual decimal ReleasedPrivateAmount { get; set; }
// more fields like this
}
public class FinancialLedger()
{
public virtual DateTime? BeginDate { get; set; }
public virtual DateTime? EndDate { get; set; }
public virtual IList<FinancialInfo> Financials { get; set; } //not actual implementation, but you get the idea
public decimal GetTotalReleasedFederalAmountByDate()
{
if (BeginDate == null && EndDate == null)
return 0;
decimal total = 0;
foreach (var fi in Financials)
{
if (someCondition)
if (someSubCondition)
total += fi.ReleasedFederalAmount;
else if (someOtherCondition)
if (someOtherSubCondition)
total += fi.ReleasedFederalAmount;
else if (anotherCondigion)
total += fi.ReleasedFederalAmount;
}
return total;
}
public decimal GetTotalReleasedNonFederalAmountByDate()
{
// same logic as above method,
// but it accesses fi.ReleasedNonFederalAmount;
}
// More methods the same as the previous, just accessing different
// members of FinancialInfo
}
My goal is to just make one method called GetTotalAmountByDate() and pass in a begin date, and end date and the name of the property (ReleasedFederalAmount or ReleasedLocalAmount, etc.) it needs to access.
I hope this depicts accurately what I'm trying to accomplish.
You don't need reflection if your properties are all numeric and can be homogeneously treated as a single type - let's say a decimal.
Something like this should do the trick:
protected decimal ComputeFinancialSum( DateTime? beginDate, DateTime? endDate,
Func<FinancialInfo,decimal> propertyToSum )
{
if (beginDate == null && endDate == null)
return 0;
decimal total = 0;
foreach (var fi in Financials)
{
if (someCondition)
if (someSubCondition)
total += propertyToSum(fi);
else if (someOtherCondition)
if (someOtherSubCondition)
total += propertyToSum(fi);
else if (anotherCondigion)
total += propertyToSum(fi);
}
return total;
}
You can then provide appropriately named versions for all of your specific cases:
public decimal GetTotalReleasedFederalAmountByDate()
{
return ComputeFinancialSum( BeginDate, EndDate,
(x) => x.ReleasedFederalAmount );
}
public decimal GetTotalReleasedNonFederalAmountByDate()
{
return ComputeFinancialSum( BeginDate, EndDate,
(x) => x.ReleasedNonFederalAmount );
}
// other versions ....
Apart from the good lamba-based suggestion from Jon Skeet, you could try something like this. (Of course, it could change the way some of your code works.)
public class ValueHolder
{
object Value;
}
public class Main
{
private ValueHolder value1 = new ValueHolder();
private ValueHolder value2 = new ValueHolder();
public Value1 { get { return value1.Value; } set { value1.Value = value; } }
public Value2 { get { return value2.Value; } set { value2.Value = value; } }
public ValueHolder CalculateOne(ValueHolder holder ...)
{
// Whatever you need to calculate.
}
public CalculateBoth()
{
var answer1 = CalculateOne(value1);
var answer2 = CalculateOne(value2);
...
}
}
This is probably the lowest-tech answer here, but why not just use a switch and merge the multiple "GetTotal...Amount" functions?
// define some enum for your callers to use
public enum AmountTypeEnum {
ReleasedFederal = 1
, ReleasedLocal = 2
}
public decimal GetTotalAmountByDate(AmountTypeEnum type)
{
if (BeginDate == null && EndDate == null)
return 0;
decimal total = 0;
foreach (var fi in Financials)
{
// declare a variable that will hold the amount:
decimal amount = 0;
// here's the switch:
switch(type) {
case AmountTypeEnum.ReleasedFederal:
amount = fi.ReleasedFederalAmount; break;
case AmountTypeEnum.ReleasedLocal:
amount = fi.ReleasedLocalAmount; break;
default: break;
}
// continue with your processing:
if (someCondition)
if (someSubCondition)
total += amount;
else if (someOtherCondition)
if (someOtherSubCondition)
total += amount;
else if (anotherCondigion)
total += amount;
}
return total;
}
This seems safer, because all your logic remains under your control (no one is passing you functions to execute).
This can be further broken down if you need to actually do different things with different amounts:
Take the processing part and turn it into a function:
private decimal ProcessNormal(decimal amount) {
decimal total = 0;
// continue with your processing:
if (someCondition)
if (someSubCondition)
total += amount;
else if (someOtherCondition)
if (someOtherSubCondition)
total += amount;
else if (anotherCondition)
total += amount;
return total;
}
public decimal GetTotalAmountByDate(AmountTypeEnum type)
{
if (BeginDate == null && EndDate == null)
return 0;
decimal total = 0;
foreach (var fi in Financials)
{
// declare a variable that will hold the amount:
decimal amount = 0;
// here's the switch:
switch(type) {
case AmountTypeEnum.ReleasedFederal:
amount = fi.ReleasedFederalAmount;
total = ProcessNormal(amount);
break;
case AmountTypeEnum.ReleasedLocal:
amount = fi.ReleasedLocalAmount;
total = ProcessNormal(amount);
break;
case AmountTypeEnum.NonReleasedOtherAmount:
amount = fi.NonReleasedOtherAmount;
total = ProcessSlightlyDifferently(amount); // for argument's sake
break;
default: break;
}
}
return total;
}