Comparing 2 Object properties in a Class - c#

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).

Related

How to check condition of xml value?

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}");

find the highest number in an array made by the method

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;
}

How to use LINQ to group objects in two groups

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.

Compare properties of two objects fast c#

I have two objects of same type with different values:
public class Itemi
{
public Itemi()
{
}
public int Prop1Min { get; set; }
public int Prop1Max { get; set; }
public int Prop2Min { get; set; }
public int Prop2Max { get; set; }
public int Prop3Min { get; set; }
public int Prop3Max { get; set; }
...................................
public int Prop25Min { get; set; }
public int Prop25Max { get; set; }
}
Now I instantiate two objects of this type and add some values to their properties.
Itemi myItem1 = new Itemi();
myItem1.Prop1Min = 1;
myItem1.Prop1Max = 4;
myItem1.Prop2Min = 2;
myItem1.Prop2Max = 4;
myItem1.Prop3Min = -1;
myItem1.Prop3Max = 5;
.............................
myItem1.Prop25Min = 1;
myItem1.Prop25Max = 5;
Itemi myItem2 = new Itemi();
myItem2.Prop1Min = 1;
myItem2.Prop1Max = 5;
myItem2.Prop2Min = -10;
myItem2.Prop2Max = 3;
myItem2.Prop3Min = 0;
myItem2.Prop3Max = 2;
................................
myItem2.Prop25Min = 3;
myItem2.Prop25Max = 6;
What is the best and fastest way to do this comparison:
take each properties from myItem1 and check if values from Prop1-25 Min and Max are within the range values of myItem2 Prop1-25 Min and Max
Example:
myItem1.Prop1Min = 1
myItem1.Prop1Max = 4
myItem2.Prop1Min = 1
myItem2.Prop1Max = 5
this is True because mtItem1 Prop1 min and max are within the range of myItem2 min and max.
the condition should be AND in between all properties so in the end after we check all 25 properties if all of them are within the range of the second object we return true.
Is there a fast way to do this using Linq or other algorithm except the traditional if-else?
I would refactor the properties to be more along the lines of:
public class Item
{
public List<Range> Ranges { get; set; }
}
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
}
Then your comparison method could be:
if (myItem1.Ranges.Count != myItem2.Ranges.Count)
{
return false;
}
for (int i = 0; i < myItem1.Ranges.Count; i++)
{
if (myItem1.Ranges[i].Min < myItem2.Ranges[i].Min ||
myItem1.Ranges[i].Max > myItem2.Ranges[i].Max)
{
return false;
}
}
return true;
Otherwise you will have to use Reflection, which is anything but fast.
Linq is using standart statements like if...then, for each and other, there is no magic :)
If the final goal only to compare, without needing to say, which properties are not in the range, then you not need to check them all, on the first unequals you can end checking.
Because you have so much properties, you must think about saving it in Dictionary, or List, for example. Or to use dynamic properties (ITypedList), if it will use for binding.
You really should do something like Ginosaji proposed.
But if you want to go with your current data model, here is how I would solve it. Happy typing. :)
public static bool RangeIsContained(int outerMin, int outerMax, int innerMin, int innerMax)
{
return (outerMin <= innerMin && outerMax >= innerMax);
}
public bool IsContained(Itemi outer, Itemi inner)
{
return RangeIsContained(outer.Prop1Min, outer.Prop1Max, inner.Prop1Min, inner.Prop1Max)
&& RangeIsContained(outer.Prop2Min, outer.Prop2Max, inner.Prop2Min, inner.Prop2Max)
// ...
&& RangeIsContained(outer.Prop25Min, outer.Prop25Max, inner.Prop25Min, inner.Prop25Max);
}
With your data model this is basically the only way to go except for reflection (slow!). LINQ cannot help you because your data is not enumerable.
For the sake of completeness, here is a LINQ solution (but it's less performant and less readable than Ginosaji's solution!)
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
public static bool IsContained(Range super, Range sub)
{
return super.Min <= sub.Min
&& super.Max >= sub.Max;
}
}
public class Itemi
{
public Itemi()
{
properties = new Range[25];
for (int i = 0; i < properties.Length; i++)
{
properties[i] = new Range();
}
}
private Range[] properties;
public IEnumerable<Range> Properties { get { return properties; } }
public static bool IsContained(Itemi super, Itemi sub)
{
return super.properties
.Zip(sub.properties, (first, second) => Tuple.Create(first, second))
.All((entry) => Range.IsContained(entry.Item1, entry.Item2));
}
public Range Prop1
{
get { return properties[0]; }
set { properties[0] = value; }
}
public Range Prop2
{
get { return properties[1]; }
set { properties[1] = value; }
}
// ...
}

How can I pass a property of a class as a parameter of a method?

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;
}

Categories