Compare properties of two objects fast c# - 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; }
}
// ...
}

Related

c# Linq Rank like TSQL Partition BY Multiple Columns

I have a List<T> and trying to rank using Linq as I can with TSQL.
TSQL: RANK() OVER ( PARTITION BY [Time], [Filter] ORDER BY [Speed] Desc) AS speedrank
After the ranks are calculated trying to update the ranked column in the List<T> with the calculated rank.
I could write the data to a sql table and query using tsql but would prefer to use the strong typed list I have in the application if it's possible.
The Rank has to take in to account duplicates Speed values based on the partitioning, so sorting and incrementing row number does not work as expected.
Updated: List in MainForm.cs
private List<Ranking> _rankingList = new List<Ranking>();
Add to list.
var advancedsearchranking = new Ranking
{
Course = course,
RaceDate = racedate,
RaceTime = racetime,
RaceDayRaceNo = racenumber,
RaceDayHorseNo = horse.HNo,
Filter = "Going",
Horse = horse.HorseName,
WinPercentage = $"{winpercentage} ({wins}/{runs})",
Positions = sbpos.ToString(),
SpeedFigures = sbspeedratings.ToString(),
LastSpeedFigure = lastspeedfigure,
Average = Math.Round((double)average, 0),
BSPs = sbbsp.ToString()
};
_rankingList.Add(advancedsearchranking);
Class:
public class Ranking
{
public string Course { get; set; }
public DateTime RaceDate { get; set; }
public TimeSpan RaceTime { get; set; }
public int? RaceDayRaceNo { get; set; }
public int? RaceDayHorseNo { get; set; }
public string Filter { get; set; }
public string Horse { get; set; }
public string WinPercentage { get; set; }
public string Positions { get; set; }
public string SpeedFigures { get; set; }
public int? LastSpeedFigure { get; set; }
public int LastSpeedRank { get; set; }
public double? Average { get; set; }
public virtual string BSPs { get; set; }
public virtual double[] BSPSparkLine { get; set; }
public double? MasterAverage { get; set; }
}
I'm try to partition by Filter property and rank by LastSpeedFigure Desc, so highest figure is ranked 1 or joint 1st if two have the same value.
Regards,
Neil
You should be able to use the following code (sorry I haven't tested any of this).
var list = _rankingList.OrderBy(r => r.Filter).ThenByDescending(r => r.LastSpeedFigure);
int rank = 1;
int rowNumber = 1;
list[0].LastSpeedRank = 1; // set the first item
for (var i = 1; i < list.Count; i++)
{
if(list[i].Filter != list[i - 1].Filter) // reset numbering
{
rank = 1;
rowNumber = 1;
}
else
{
rowNumber++; // row-number always counts up
if(list[i].LastSpeedFigure != list[i - 1].LastSpeedFigure)
rank = rowNumber; // only change rank if not tied
}
list[i].LastSpeedRank = rank;
}
You can also implement an IEnumerable extension to do this
public T WithRank<T>(
this IEnumerable<T> source,
Func<T, T, bool> partitioning,
Func<T, T, bool> ordering,
Action<T, int> setRank)
{
using var enumer = source.GetEnumerator();
if(!enumer.MoveNext())
yield break;
var previous = enumer.Current;
setRank(previous, 1);
yield return previous;
int rank = 1;
int rowNumber = 1;
while(enumer.MoveNext())
{
if(!partitioning(enumer.Current,previous)) // reset numbering
{
rank = 1;
rowNumber = 1;
}
else
{
rowNumber++; // row-number always counts up
if(ordering(enumer.Current, previous))
rank = rowNumber; // only change rank if not tied
}
setRank(previous, rank);
yield return enumer.Current;
}
}
Use it like this
// the list must be pre-sorted by partitioning values then ordering values.
var list = _rankedList
.OrderBy(r => r.Filter)
.ThenByDescending(r => r.LastSpeedFigure)
.WithRanking(
(a, b) => a.Filter == b.Filter,
(a, b) => a.LastSpeedFigure == b.LastSpeedFigure,
(o, rank) => { o.LastSpeedRank = rank; })
.ToList();
You can implement ROW_NUMBER by just using the rowNumber variable and removing the conditional rank = rowNumber; statement. You can implement DENSE_RANK by changing that line to rank++; and ignoring rowNumber.

Set Class object using List?

I have a class with n data members as follows :
public class Input
{
int mode1 {get; set;}
int geom2 {get; set;}
int type3 {get; set;}
int spacing4 {get; set;}
int fluid5 {get; set;}
int spec6 {get; set;}
...
...
...
int data_n {get; set;}
}
and I have a filled list of n int items.
List<int> dataList = new List<int>
Now I want to fill an object of class Input from dataList through iteration or through any other direct method will be helpful. Thank you
Reflection can be useful here but you should definitely re-consider your design if you have 98 properties in one class.
var properties = typeof(Input)
.GetProperties()
.Where(p => p.PropertyType == typeof(int));
int i = 0;
foreach(var prop in properties)
prop.SetValue(yourObject, dataList[i++]);
But this doesn't guarantee that each property will be assigned correctly because as already mentioned by #CodeCaster GetProperty method doesn't return the properties in particular order.And there is no way to determine the order and map values to the properties when you have List<int>, if you can use Dictionary<string, int> instead, where key is the property name, then you can set each property to the corresponding value.
You could try something like this (object initializer):
Input input = new Input { mode1 = dataList[0],
geom2 = dataList[1],
type3 = dataList[2],
spacing4 = dataList[3],
fluid5 = dataList[4],
spec6 = dataList[5] };
You can do this from reflection using GetProperties method as others said but why not use a simple way to do this?
Input i = new Input();
i.mode1 = dataList[0];
i.geom2 = dataList[1];
i.type3 = dataList[2];
i.spacing4 = dataList[3];
i.fluid5 = dataList[4];
i.spec6 = dataList[5];
You can try something like this using the reflection :
var properties = typeof(Input).GetProperties();
for(int i = 0; i < properties.Count(); ++i)
{
properties[i].SetValue(dataList[i]);
}
I'm not sure how you use your properties but you could alternatively have a list in your class and then set the whole list or add to that. Then you could use an enum to keep track of the values.
Something like this:
public class Input
{
public enum MyValues
{
mode1 = 1,
geom2 = 2,
...
}
public List<int> Data { get; set; }
}
class Layout
{
int mode1 { get; set; }
int geom2 { get; set; }
int type3 { get; set; }
int spacing4 { get; set; }
int fluid5 { get; set; }
int spec6 { get; set; }
public int this[int number]
{
get
{
if (number == 1)
return mode1;
else if (number == 2)
return geom2;
else if (number == 3)
return type3;
else if (number == 4)
return spacing4;
else if (number == 5)
return fluid5;
else if (number == 6)
return spec6;
else
return -1;
}
set
{
if (number == 1)
mode1 = value;
else if (number == 2)
geom2 = value;
else if (number == 3)
type3 = value;
else if (number == 4)
spacing4 = value;
else if (number == 5)
fluid5 = value;
else if (number == 6)
spec6 = value;
}
}
iteration code
Layout layout = new Layout();
foreach(int i in dataList)
{
layout[i]=dataList[i];
}

Give an Array a Value

I am trying to make a game where an image appears, and if it is not clicked the image should disappear. I need help giving my array a value of three, then subtract it in another method.
Code:
NameCount = -1;
NameCount++;
Grid.SetColumn(mole, ranCol);
Grid.SetRow(mole, ranRow);
grid_Main.Children.Add(mole);
for (int i = 0; i < NumofImages; i++)
{
//Where I must give a value to the array of the array to 3 for every image that appears.
}
//Where I am trying to make the image disappear after 3 seconds.
private void deleteMole()
{
NumofImages = TUtils.GetIniInt(Moleini, "NumPictures", "pictures", 8);
NumberofImages = Convert.ToInt32(NumofImages);
for (int j = 0; j < NumofImages; j++)
{
CounterArray[j]--;
if (CounterArray[j] == 0)
{
//Not Sure How to delete image
Thanks for the help!
You could keep track of the images in another array.
After you add the image to the view you should also add it to the array:
images[j] = mole;
Then later:
if (CounterArray[j] == 0)
{
grid_Main.Children.Remove(images[j]);
}
But using static arrays and separating data is not a good idea.
If you can you should better aggregate all the metadata and the image together in the same structure:
class Mole
{
public int Counter { get; set; }
public Control Image { get; set; }
}
and manage them in a single List<Mole>; adding and removing them will be simpler.
Here is some code that illustrates the idea (won't compile):
class Mole
{
public int X { get; set; }
public int Y { get; set; }
public int Counter { get; set; }
public Control Image { get; set; }
public bool IsNew { get; set; }
}
class Test
{
IList<Mole> moles = new List<Mole>();
private static void AddSomeMoles()
{
moles.Add(new Mole{ X = rand.Next(100), Y = rand.Next(100), Counter = 3, Image = new PictureBox(), IsNew = true });
}
private static void DisplayMoles()
{
foreach (Mole mole in moles)
{
if (mole.IsNew)
{
grid_Main.Children.Add(mole.Image);
mole.IsNew = false;
}
}
}
private static void CleanupMoles()
{
foreach (Mole mole in moles)
{
mole.Counter -= 1;
if (mole.Counter <= 0)
{
grid_Main.Children.Remove(mole.Image);
moles.Remove(mole);
}
}
}
static void Main()
{
while (true)
{
AddSomeMoles();
DisplayMoles();
Thread.Sleep(1000);
CleanupMoles();
}
}
}
If you want to give every element in a List a certain value, use a foreach loop. In this case, it would look like:
foreach(int currentElement in CounterArray)
{
currentElement = 3;
}
This will loop through each element of the List and set it to 3.
EDIT: If you're using an array, which you are, you would do the following:
for (int i = 0; i < CounterArray.Length; i++)
{
CounterArray[i] = 3;
}

Linq To Objects C#

I want to get a collection of Bonds and their associate collection of maturities where the maturity.matamount > 200000.
edit:(I only want the maturity collection of each bond to include maturities > 200000)
Here is a program.cs that has the class defs and a method to populate test data for the query.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LinqToObjects
{
class Program
{
static void Main(string[] args)
{
Program.QueryCollection();
Console.ReadLine();
}
public static void QueryCollection()
{
List<Bond> bonds = Program.BuildCollections();
//how do I get a list of Bonds that have a maturity.MatAmount > 200,000?
}
public static List<Bond> BuildCollections()
{
List<Bond> bonds = new List<Bond>();
Bond bond;
for (int i = 1; i <= 10; i++)
{
bond = new Bond() {ID = i, Title = "Bond Title " + i.ToString() };
for (int j = 1; j <= 10; j++)
{
bond.Maturities.Add(new Maturity(){ID = j, BondID = i, MatDate = DateTime.Today.AddDays(j), MatAmount = 152000 * j});
}
bonds.Add(bond);
}
return bonds;
}
}
public class Bond
{
public int ID { get; set; }
public string Title { get; set; }
public List<Maturity> Maturities { get; set; }
public Bond()
{
Maturities = new List<Maturity>();
}
}
public class Maturity
{
public int ID { get; set; }
public int BondID { get; set; }
public DateTime MatDate { get; set; }
public int MatAmount { get; set; }
}
}
How about this?
IEnumerable<Bond> bigBonds = bonds.Where(b => b.Maturities.Any(m => m.MatAmount > 200000));
I'm not exactly sure what you are looking for but to get your bonds just do something like this:
var filteredBonds =
(
from bond in bonds
join maturity in maturities on bond.ID equals maturity.BondID
where maturity.MatAmount > 200000
select new { bond.ID, bond.Title, maturity.MatAmount, maturity.MatDate }
).ToList();
The ToArray() is optional, you could leave things in the table form if you like. I'm not really sure what you want to do with your results.
You might also want to decouple your Bonds and Maturities in your data structure. I don't think you really need to have a list of Maturities is you are storing the BondID as a member of a maturity object. It seems a little redundant, but honestly I don't use Linq so maybe that is the way to go. Your call.

select child object collection with lambdas

I have the following class objects:
public class VacancyCategory
{
public int ID { get; set; }
public string Text { get; set; }
public IList<VacancySubCategory> SubCategories { get; set; }
}
public class VacancySubCategory
{
public int ID { get; set; }
public string Text { get; set; }
public VacancyCategory Category { get; set; }
public IList<Vacancy> Vacancies { get; set; }
}
public class Vacancy : IBusinessObject
{
public int ID { get; set; }
public string Title { get; set; }
public VacancySubCategory SubCategory { get; set; }
public string Body { get; set; }
public VacancyWorkType WorkType { get; set; }
public string Salary { get; set; }
public DateTime? AppsClosingDate { get; set; }
public bool Active { get; set; }
}
...so in a test repository im creating test data like so:
private IList<VacancyCategory> GetVacancyCategoriesWithAllChildCollections()
{
IList<VacancyCategory> vacancyCategories = new List<VacancyCategory>();
int cCounter = 0;
int scCounter = 0;
int vCounter = 0;
for (int i = 1; i <= 3; i++)
{
VacancyCategory vc = new VacancyCategory();
vc.ID = ++cCounter;
vc.Text = "VacancyCategory" + i.ToString();
for (int j = 1; j <= 3; j++)
{
VacancySubCategory vsc = new VacancySubCategory();
vsc.ID = ++scCounter;
vsc.Text = "VacancySubCategory" + scCounter.ToString();
vsc.Category = vc;
for (int k = 1; k <= 2; k++)
{
Vacancy v = new Vacancy();
v.ID = ++vCounter;
v.Title = "Vacancy" + vCounter.ToString();
v.Body = "VacancyBody" + vCounter.ToString();
v.Active = vCounter >= 16 ? false : true;
v.WorkType = this._workTypes.Single(wt => wt.ID == k);
v.Salary = vCounter <= 7 ? "SR " + (vCounter * 1000).ToString() : "";
v.AppsClosingDate = (vCounter >= 3 & vCounter <= 13) ? (new DateTime(2009, 3, vCounter)) : (DateTime?)null;
v.SubCategory = vsc;
if (vsc.Vacancies == null)
vsc.Vacancies = new List<Vacancy>();
vsc.Vacancies.Add(v);
}
if (vc.SubCategories == null)
vc.SubCategories = new List<VacancySubCategory>();
vc.SubCategories.Add(vsc);
}
vacancyCategories.Add(vc);
}
return vacancyCategories;
}
..so now i have some good test data. the object tree / chained objects are important to me.
so i'd like to return the individual object collections from this tree when desired. for example, if i wanted the whole tree, i can just return the VacancyCategory list with all the child objects - great. but now i want to return just the VacancySubCaregory items (all 9 of them). this would be my public method to the test repository:
public IQueryable<VacancySubCategory> GetVacancySubCategories()
{
throw new NotImplementedException("write gen code");
}
.. obviously without the exception. i have a member field called _categories that contains the results from the GetVacancyCategoriesWithAllChildCollections method. so i've been trying stuff like
this._categories.Select( ......
..but i cant seem to return a list of VacancySubCategory objects. i seem to always be selecting the root collection (ie. a result set of VacancyCategory objects). What am i doing wrong? im sure its simple... but its driving me nuts!
EDIT
thanx matt.
your suggestion led me to this:
public IQueryable<VacancySubCategory> GetVacancySubCategories()
{
return this._categories.SelectMany(c => c.SubCategories).AsQueryable<VacancySubCategory>();
}
..which works great. you're a champ
Try:
return this._categories.SelectMany(c => c.SubCategories);
This should work.
var query = from vc in GetVacancyCategoriesWithAllChildCollections()
from vcs in vc.SubCategories
select vcs

Categories