linq Groupby a list by "sublist" - c#

I have a List<Meb> (a bar nesting), each of these nestings have a list of details inside.
All of these bars are unique, because each of element inside is unique by its ID.
Now I want to add a checkbox, in order to group or not all bars that have the same list of details inside (the list of items inside are identical, except their ID, and some parameters I first set to -1 or ""). Here is the function I made in order to do that :
private List<Meb> GroupIdenticalMeb(List<Meb> mebInput)
{
List<Meb> retour = new List<Meb>();
foreach(Meb mebOri in mebInput)
{
Meb meb = new Meb();
meb.ID = -1;
meb.Number = mebOri.Number;
meb.Length = mebOri.Length;
meb.Quantity=mebOri.Quantity;
foreach(Repere repOri in mebOri.ListReperes)
{
Repere rep = new Repere();
rep.Name = repOri.Name;
rep.Quantite = repOri.Quantite;
rep.ID = -1;
meb.ListReperes.Add(rep);
}
retour.Add(meb);
}
retour = retour.GroupBy(l => l.ListReperes)
.Select(cl => new Meb
{
ID=-1,
Number = cl.First().Number,
Length = cl.First().Length,
Quantity=cl.Sum(c => c.Quantity),
ListReperes = cl.First().ListReperes,
}).ToList();
return retour;
}
The idea is that:
1st: I create a new List<Meb> that copies the original List<Meb>, for the List<Repere>, I also copy it, but setting the ID to "-1", as others properties that could differ between them.
2nd: I make a group by on the List<Repere>
But on the end no groupby is done, and the output remains the same as the input.
Edit :
I explain better the structure of my objects because it seems it was not clear enough :
Each Meb object represents a beam, each beams contains Repere objects(details), these details have a lot of parameters, most importants are ID, Name, Quantity, concrete example :
ID Name Quantity
Meb1(Quantity1) contains : 11 Repere1 2
20 Repere2 1
25 Repere3 1
Meb2(Quantity2) contains : 12 Repere1 2
24 Repere2 2
28 Repere3 1
Meb3(Quantity3) contains : 31 Repere1 2
18 Repere2 1
55 Repere3 1
So I import my List<Meb>, and I want to group all my Mebs, comparing their details list.
In that case the result would be :
Meb1(Quantity4) contains : 0 Repere1 2
0 Repere2 1
0 Repere3 1
Meb2(Quantity2) contains : 0 Repere1 2
0 Repere2 2
0 Repere3 1

I would recommend that you add some sort of property in your Meb class that hashes all of your ListReperes items, and then group off that.
You can have a look at this link: How to generate a unique hash for a collection of objects independent of their order
IE then you would do:
retour = retour.GroupBy(l => l.HashReperes) and this would provide you a unique grouped list of your lists.
where HashReperes is the property that provides the Hash of the Reperes List.

Use IEquatable. Then you can use the standard linq GroupBy(). See code below
public class Meb : IEquatable<Meb>, INotifyPropertyChanged
{
public int ID { get; set; }
public int Number { get; set; }
public int Length { get; set; }
public int Quantity { get; set;}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private List<Meb> GroupIdenticalMeb(List<Meb> mebInput)
{
return mebInput.GroupBy(x => x).Select(x => new Meb() {
ID = x.First().ID,
Number = x.First().Number,
Length = x.First().Length,
Quantity = x.Sum(y => y.Quantity)
}).ToList();
}
public bool Equals(Meb other)
{
if ((this.Number == other.Number) && (this.Length == other.Length) && (this.Quantity == other.Quantity))
{
return true;
}
else
{
return false;
}
}
public override int GetHashCode()
{
return ID;
}
}
If you don't want to use IEquatable then use this
private List<Meb> GroupIdenticalMeb(List<Meb> mebInput)
{
return mebInput.GroupBy(x => new { number = x.Number, len = x.Length }).Select(x => new Meb()
{
ID = x.First().ID,
Number = x.Key.number,
Length = x.Key.len,
Quantity = x.Sum(y => y.Quantity)
}).ToList();
}
For comparing a List use something like this
public class MyClassA : IEquatable<List<MyClassB>>
{
public List<MyClassB> myClassB { get; set; }
public bool Equals(List<MyClassB> other)
{
if(other == null) return false;
if (this.myClassB.Count() != other.Count()) return false;
var groupThis = this.myClassB.OrderBy(x => x.propertyA).ThenBy(x => x.propertyB).GroupBy(x => x).ToList();
var groupOther = other.OrderBy(x => x.propertyA).ThenBy(x => x.propertyB).GroupBy(x => x).ToList();
if (groupThis.Count() != groupOther.Count) return false;
for (int i = 0; i < groupThis.Count(); i++)
{
if (groupThis[i].Count() != groupOther[i].Count()) return false;
}
return true;
}
public override int GetHashCode()
{
return 0;
}
}
public class MyClassB : IEquatable<MyClassB>
{
public int propertyA { get; set; }
public string propertyB { get; set; }
public bool Equals(MyClassB other)
{
if (other == null) return false;
if ((this.propertyA == other.propertyA) && (this.propertyB == other.propertyB))
{
return true;
}
else
{
return false;
}
}
public override int GetHashCode()
{
return 0;
}
}

On the end, here is the way I could solve the problem :
private List<Meb> GroupIdenticalMeb(List<Meb> mebInput)
{
List<Meb> retour = new List<Meb>();
foreach(Meb mebOri in mebInput)
{
Meb meb = new Meb();
meb.ID = -1;
meb.Number = mebOri.Number;
meb.Length = mebOri.Length;
meb.Quantity=mebOri.Quantity;
foreach(Repere repOri in mebOri.ListReperes)
{
Repere rep = new Repere();
rep.Name = repOri.Name;
rep.Quantite = repOri.Quantite;
rep.ID = -1;
meb.ListReperes.Add(rep);
}
retour.Add(meb);
// Here I added a string property, in which I concatenate
//name and quantity of each Repere in my List<Repere>,
//so on the end the "SomeString" parameters will be identical
//for all Meb that have the same List<Repere> (ignoring the IDs).
foreach(Meb meb in retour)
{
meb.SomeString = "";
foreach(RepereNest rep in meb.ListReperes)
{
meb.SomeString += rep.Name + rep.Quantite;
}
}
}
retour = retour.GroupBy(l => l.SomeString)
.Select(cl => new Meb
{
ID=-1,
Number = cl.First().Number,
SomeString=cl.First().SomeString,
Length = cl.First().Length,
Quantity=cl.Sum(c => c.Quantity),
ListReperes = cl.First().ListReperes,
}).ToList();
return retour;
}
Well for now ths is the only way I could find to group not on my parameters(for this no problem), but on parameters inside a List of my object. And I think this method is not so bad, because I also have Lists inside of Repere objects, so I could use the same tip in future. On the end I just don't understand why it is not possible to check when Lists of my objects are equals?

Related

Make a specific Sum<T>

I have a class Contract with inside an ObservableCollection<Repere>:
class Contract : INotifyPropertyChanged
{
private ObservableCollection<Repere> listReperes;
public ObservableCollection<Repere> ListReperes
{
get { return listReperes; }
set
{
listReperes = value;
NotifyPropertyChanged(ref listReperes, value); //NotifyPropertyChanged("ListReperes");
}
}
...
}
My class 'Repere' has an ObservableCollection<Operation>
class Repere : INotifyPropertyChanged
{
private ObservableCollection<Operation> listOperations;
public ObservableCollection<Operation> ListOperations
{
get { return listOperations; }
set
{
NotifyPropertyChanged(ref listOperations, value);
}
...
}
}
Finally, my class Operation is as the following :
public class Operation : INotifyPropertyChanged
{
private int qtyFinished;
public int QtyFinished
{
get { return qtyFinished; }
set
{
qtyFinished = value;
this.NotifyPropertyChanged("QtyFinished");
}
}
public long ID { get; set; }
...
}
My contract has a list of Repere, and Each Repere has inside a list of operations,what I want is to group my Repere by name, and get the total list of Operation for each Repere.
For now I do the following :
List<Repere> liste_rep_group = liste_rep.GroupBy(l => l.Name)
.Select(cl => new Repere
{
Quantite = cl.Sum(c => c.TotalQuantity),
TotalQuantity = cl.Sum(c => c.TotalQuantity),
ID = -1,
IdAff = cl.First().IdAff,
Name = cl.First().Name,
NameOri = cl.First().Name,
Nom_aff = cl.First().Nom_aff,
Profil = cl.First().Profil,
Longueur = cl.First().Longueur,
Hauteur = cl.First().Hauteur,
Largeur = cl.First().Largeur,
Poids = cl.First().Poids,
Priorite = cl.Min(c => c.Priorite),
ListOperations = new ObservableCollection<Operation>(),
}).ToList();
And as I don't know how to "sum" the ListOperations, I then do it manually
foreach (Repere rep in liste_rep)
{
Repere repFound = liste_rep_group.FirstOrDefault(x => x.Name == rep.Name);
if (repFound != null)
{
foreach (Operation op in rep.ListOperations)
{
Operation opFound = repFound.ListOperations.FirstOrDefault(y => y.ID == op.ID);
if (opFound != null)
{
opFound.QtyFinished += op.QtyFinished;
opFound.QtyTot += op.QtyTot;
}
else
{
Operation newOp = new Operation();
newOp.Nom = op.Nom;
newOp.ID = op.ID;
newOp.Clone(op);
repFound.ListOperations.Add(newOp);
}
}
}
}
What I want is :
Group my ObservableCollection by Repere name.
Sum the list of operations for each Repere with same name
The sum may work like that:
public ObservableCollection<Operation> Sum(operation1, operation2)
{
ObservableCollection<Operation> mySum=new ObservableCollection<Operation>();
if(operation1.ID==operation2.ID)
{
operation1.QtyFinished+=operation2.QtyFinished;
mySum.Add(operation1);
return mySum;
}
else
{
mySum.Add(operation1);
mySum.Add(operation2);
return mySum;
}
}
Is there a way to define in my Operation class, a Sum function where I would define how to sum my ObservableCollection?
Edit : Here is an example as requested:
If I want to sum 2 Repere, the result summing operations will be something like that :
Repere1
ID_operation Quantity
1 2
2 1
3 5
Repere1
ID_operation Quantity
1 2
2 1
4 2
Result will be :
Repere1
ID_operation Quantity
1 4
2 2
3 5
4 2
EDIT :
Ok, all is working fine, just need to change it for ObservableCollection :
ListOperations = new ObservableCollection<Operation>(cl.SelectMany(g=>g.ListOperations).GroupBy(o=>o.ID)
.Select(go => new Operation
{
ID = go.First().ID,
QtyFinished = go.Sum(o => o.QtyFinished),
Color=go.First().Color,
}))
Here's one approach:
class Repere
{
public string Name { get; set; }
public List<Operation> Operations { get; set; }
}
class Operation
{
public int Id { get; set; }
public int Quantity { get; set; }
}
class Program
{
static void Main(string[] args)
{
var data = new List<Repere>
{
new Repere { Name = "Repere1", Operations = new List<Operation>
{
new Operation { Id = 1, Quantity = 2 },
new Operation { Id = 2, Quantity = 1 },
new Operation { Id = 3, Quantity = 5 }
}},
new Repere { Name = "Repere1", Operations = new List<Operation>
{
new Operation { Id = 1, Quantity = 2 },
new Operation { Id = 2, Quantity = 1 },
new Operation { Id = 4, Quantity = 2 }
}},
};
var result = data.GroupBy(r => r.Name)
.Select(gr => new
{
Name = gr.Key,
Operations = gr.SelectMany(g => g.Operations)
.GroupBy(o => o.Id)
.Select(go => new { Id = go.Key, Quantity = go.Sum(o => o.Quantity)})
});
foreach (var r in result)
{
Console.WriteLine(r.Name);
foreach (var o in r.Operations)
{
Console.WriteLine($"\t{o.Id}\t{o.Quantity}");
}
}
Console.ReadKey();
}
}
Also, you don't need to have a setter on ListOperations in order for changes to the collection to be properly notified to the UI - the individual items (that implement INotifyPropertyChanged) will raise their own notifications (ie. these won't use the collection's set method).

Best Practice to find best matching instance in a List C#

For sure very simple question for most of you.
But I am struggling with a solution at the moment.
Imagine you have a list of cats (List) where each cat has a list of babys (Kitten)
public class Cat
{
public string Name { get; set; }
public int Age { get; set; }
public string Race { get; set; }
public bool Gender { get; set; }
public List<Kitten> Babys { get; set; }
}
public class Kitten
{
public string Name { get; set; }
public double Age { get; set; }
public bool Gender { get; set; }
}
now I want to find the Cat that has the most matches for given requirements. It could easily be the case that a cat matches only 2 of 3 requirements. I simple want to find the cat that has the most matches to my requirements.
where my requirements could be:
Name has to be "Micky"
Age is 42
Has a Kitten named "Mini"
My actual solution would be to compare all properties and take the one with the highest count of matching properties. But this is not generic and I am sure there are mutch better ways to do it.
Thanks in advance
Well, I have no opportunity to test this solution, but you can try this:
Assume that you have a list of cats:
var cats = new List<Cat>();
Now you have defined what are your criteria:
var desiredName = "Micky";
var desiredAge = 42;
var desiredKitten = "Mini";
And then you have to get your desired cat:
var desiredCat = cats
.Select(c => new {
Rating =
Convert.ToInt32(c.Age == desiredAge) + // Here you check first criteria
Convert.ToInt32(c.Name == desiredName) + // Check second
Convert.ToInt32(c.Babys.Count(b => b.Name == desiredKitten) > 0), // And the third one
c })
.OrderByDescending(obj => obj.Rating) // Here you order them by number of matching criteria
.Select(obj => obj.c) // Then you select only cats from your custom object
.First(); // And get the first of them
Please check if this works for you.
And if you need more specific answer or some edits for me to add.
If you really will compare 2 ou 3 requirements you can simplify using Linq by:
// try to find with 3 requirements
var foundCats = catList.Where(t => t.Name == desiredName &&
t.Age == desiredAge &&
t.Babys.Any(k => k.Name == desiredKitten)
).ToList();
if (foundCats.Any())
{
// you found the desired cat (or cats)
return foundCats;
}
// try to find with 2 requirements
foundCats = catList.Where(t =>
(t.Name == desiredName && t.Age == desiredAge) ||
(t.Name == desiredName && t.Babys.Any(k => k.Name == desiredKitten)) ||
(t.Age == desiredAge && t.Babys.Any(k => k.Name == desiredKitten)
).ToList();
if (foundCats.Any())
{
// you found the desired cat (or cats)
return foundCats;
}
// try to find with only 1 requirement
foundCats = catList.Where(t => t.Name == desiredName ||
t.Age == desiredAge ||
t.Babys.Any(k => k.Name == desiredKitten)
).ToList();
return foundCats;
So, I see that the problem is you don't know if in any near future you will have more properties, so I will suggest going to the hardway and make reflection, the following is ugly af but you can probably (you will) make it better and hopefully serves you well as guiadance:
public static List<Cat> CheckProperties(List<Cat> inCatList, Cat inQueryCat)
{
Dictionary<Cat, List<PropertyInfo>> dict = new Dictionary<Cat, List<PropertyInfo>>();
foreach (PropertyInfo pI in inQueryCat.GetType().GetProperties())
{
var value = pI.GetValue(inQueryCat);
if (value != null)
{
var cats = inCatList.Where(cat => cat.GetType().GetProperty(pI.Name).GetValue(cat).Equals(value));
foreach (Cat cat in cats)
{
if (dict.ContainsKey(cat))
{
dict[cat].Add(pI);
}
else
{
dict.Add(cat, new List<PropertyInfo>() {pI});
}
}
}
}
int max = Int32.MinValue;
foreach (KeyValuePair<Cat, List<PropertyInfo>> keyValuePair in dict)
{
if (keyValuePair.Value.Count > max)
{
max = keyValuePair.Value.Count;
}
}
return dict.Where(pair => pair.Value.Count == max).Select(pair => pair.Key).ToList();
}
While this is the most generic solution there is (need some edge case improvements):
public class ReflectCmpare
{
public PropertyInfo PropertyInfo { get; set; }
public dynamic Value { get; set; }
}
public Cat GetBestCat(List<Cat> listOfCats, List<ReflectCmpare> catParamsToCompare, List<ReflectCmpare> kittensParamsToCompare)
{
var bestScore = 0;
var ret = listOfCats[0];
foreach (var cat in listOfCats)
{
var score = catParamsToCompare.Sum(param => param.PropertyInfo.GetValue(cat, null) == param.Value ? 1 : 0);
foreach (var baby in cat.Babys)
{
score+= kittensParamsToCompare.Sum(param => param.PropertyInfo.GetValue(baby, null) == param.Value ? 1 : 0);
}
if (score <= bestScore) continue;
bestScore = score;
ret = cat;
}
return ret;
}
You should really think about just doing simple compare function
considering this objects is not dynamic this is the way to go:
public Cat GetBestCat(List<Cat> listOfCats, string name , int? age , bool? gender, string race ,string babyName,int? babyAge,bool? babyGender )
{
var ret = listOfCats[0];
var highestScore = 0;
foreach (var cat in listOfCats)
{
var score = 0;
score += name != null && cat.Name.Equals(name) ? 1 : 0;
score += age.HasValue && cat.Age.Equals(age.Value) ? 1 : 0;
score += gender.HasValue && cat.Gender.Equals(gender.Value) ? 1 : 0;
score += race != null && cat.Race.Equals(race) ? 1 : 0;
score += name != null && cat.Name.Equals(name) ? 1 : 0;
score += cat.Babys
.Where(k => babyName==null || k.Name.Equals(babyName))
.Where(k => !babyAge.HasValue || k.Age.Equals(babyAge.Value))
.Any(k => !babyGender.HasValue || k.Gender.Equals(babyGender.Value))?1:0;
if (score <= highestScore) continue;
highestScore = score;
ret = cat;
}
return ret;
}

Sort List with Linq, with groups alphabetic

I have a class Guest
class Guest
{
bool f = true;
bool g = false;
bool s = false;
string name = "";
}
And a List where all the included -> var g = new List<Guest>();
It can be only one of the booleans true.
At the start come all the f guest then g guest in the middle and at last the s guest.
But all guest must be sorted alphabetic in f or g or in the a group.
Maybe so?
var query = (from Guest in GuestList
orderby Guest.f, Guest.g, Guest.s, Guest.name
select Guest);
I'm just not it.
Thanks and greetz, Malte
Sounds like pretty typical nested sorting. There is no need to group.
var result = source
.OrderBy(guest =>
guest.f ? 1 :
guest.g ? 2 :
guest.s ? 3 :
4)
.ThenBy(guest => guest.name);
For those who are unfamiliar with the syntax, allow me to read the code.
In the call to OrderBy, there is a lambda function which uses a chained ternary operator to generate a sort key for each row. OrderBy sorts by this sort key.
The result of OrderBy is an IOrderedEnumerable<Guest> and is passed to ThenBy.
ThenBy preserves the sorting of the prior ordering operations and works to break ties.
This should work. The Sort and OrderBy will use the CopmpareTo() method
public class Guest : IComparable<Guest>
{
public bool f { get; set; }
public bool g { get; set; }
public bool s { get; set; }
public string name { get; set; }
public int CompareTo(Guest other)
{
int results = 0;
if (this.f)
{
if (other.f)
{
results = this.name.CompareTo(other.name);
}
else
{
results = 1;
}
}
else
{
if (other.f)
{
results = -1;
}
else
{
if (this.g)
{
if (other.g)
{
results = this.name.CompareTo(other.name);
}
else
{
results = 1;
}
}
else
{
if (other.g)
{
results = -1;
}
else
{
if (this.s)
{
if (other.s)
{
results = this.name.CompareTo(other.name);
}
else
{
results = 1;
}
}
else
{
results = this.name.CompareTo(other.name);
}
}
}
}
}
return results;
}
Below is simpler method which will even work with when more than one property is true. Notice that I used 1,2,4 instead of 1,2,3 as in other solutions. 1,2,3 has issue that there is more than one way of getting 3 when multiple properties are true.
public class Guest : IComparable<Guest>
{
public bool f { get; set; }
public bool g { get; set; }
public bool s { get; set; }
public string name { get; set; }
public int CompareTo(Guest other)
{
int results = 0;
int thisRank = (this.f ? 1 : 0) + (this.g ? 2 : 0) + (this.s ? 4 : 0);
int otherRank = (other.f ? 1 : 0) + (other.g ? 2 : 0) + (other.s ? 4 : 0);
if (thisRank == otherRank)
{
results = this.name.CompareTo(other.name);
}
else
{
results = thisRank.CompareTo(otherRank);
}
return results;
}
}
here is David B's example with more common syntax for the if statements.
var result = source
.OrderBy(guest => { if (guest.f == true) return 1 else
if (guest.g == true) return 2 else
if (guest.s == true) return 3 else return 4;})
.ThenBy(guest => guest.name);

List<object> select use multiple values

I want to find a specific value from a List with the method select.
My code :
public class Calc
{
public int IdCalc { get; set; }
public double Result { get; set; }
public int Number { get; set; }
}
public class Program
{
static void Main()
{
Calc myC1 = new Calc();
List<Calc> liCalc = new List<Calc>();
myC1.IdCalc = -1;
myC1.Result = 20.2;
myC1.Number = 1;
Calc myC2 = new Calc();
myC2.IdCalc = 22;
myC2.Result = 20.2;
myC2.Number = 2;
liCalc.Add(myC1);
liCalc.Add(myC2);
double getResult = ((Calc)(liCalc.Select(Calc => Calc.IdCalc = 22 && Calc.Number = 2))).Result;
Console.ReadKey();
}
}
As you can see my List contains two objects: myC1 and myC2.
I just want to find the value of Result when IdCalc = 22 and Number = 2 thats why I tried to use Select but it's not working with two parameters.
You could use Where, which lets you filter results based on some criteria, however that will return an IEnumerable<Calc>. Since you are only looking for a single result, you should use First which also takes a predicate and only returns the first Calc:
Calc myCalc = liCalc.First(c => c.IdCalc == 22 && c.Number == 2);
double result = myCalc.Result;
This will throw an exception if there is nothing that matches the filter, though. If you're worried about that, use FirstOrDefault which will return null if there is no match.
public class Calc
{
public int IdCalc { get; set; }
public double Result { get; set; }
public int Number { get; set; }
}
public class Program
{
static void Main()
{
Calc myC1 = new Calc();
List<Calc> liCalc = new List<Calc>();
myC1.IdCalc = -1;
myC1.Result = 20.2;
myC1.Number = 1;
Calc myC2 = new Calc();
myC2.IdCalc = 22;
myC2.Result = 20.2;
myC2.Number = 2;
liCalc.Add(myC1);
liCalc.Add(myC2);
double getResult = liCalc.First(item => item.IdCalc == 22 && item.Number == 2).Result; //Note that this will throw an exception if no item in the list satisfies the condition.
Console.ReadKey();
}
You could use the following statement
double getResult = liCalc.Where(Calc => Calc.IdCalc = 22 && Calc.Number = 2))).Select(y=>y.Result).FirstOrDefault();
Essentially using Where() followed by Select().

Using Contains() to check multiple object values exist in list before adding new list?

How do I check for multiple properties in object to see if it exists in list before adding new object to list? The Contains() doesn't work here, so what should work instead?
What I have here is a large list model and I want to port some data over to a new shorter list model having no duplication values, having unique values.
Thanks..
public class Fee {
public string Year { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string Vin { get; set; }
public string Color { get; set; }
}
public class Foo {
public string Year { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
List<Fee> modelVehicles = new List<Fee>();
List<Foo> returnVehicles = new List<Foo>();
modelVehicles.Add(new Fee() { Year = "2001", Make = "Ford", Model = "Mustang", Vin = "111", Color = "Green" });
modelVehicles.Add(new Fee() { Year = "2001", Make = "Ford", Model = "Mustang", Vin = "222", Color = "Red" });
modelVehicles.Add(new Fee() { Year = "2001", Make = "Ford", Model = "Mustang", Vin = "333", Color = "Black" });
foreach(var v in modelVehicles)
{
if (returnVehicles.Contains(new Foo { Year = v.Year, Make = v.Make, Model = v.Model }) == false)
{
returnVehicles.Add(
new Foo {
Year = v.Year,
Make = v.Make,
Model = v.Model
}
);
}
}
You can use GroupBy operator, because in fact your operation is grouping:
returnVehicles = modelVehicles.GroupBy(x => new { x.Year, x.Make, x.Model },
x => new Foo() {
Year = x.Year,
Make = x.Make,
Model = x.Model
},
(key, values) => values.First())
.ToList();
And if you implement Equals and GetHashCode for Foo:
public class Foo
{
public string Year { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public override bool Equals(object obj)
{
Foo other = obj as Foo;
if (other == null)
return false;
return string.Equals(Year, other.Year) &&
string.Equals(Make, other.Make) &&
string.Equals(Model, other.Model);
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + Year.GetHashCode();
hash = (hash * 7) + Make.GetHashCode();
hash = (hash * 7) + Model.GetHashCode();
return hash;
}
}
this can be simplified:
returnVehicles = modelVehicles.GroupBy(x => new Foo() {
Year = x.Year,
Make = x.Make,
Model = x.Model
},
(key, values) => key)
.ToList();
If all you want to do is get a list of the distinct items, then use Enumerable.Distinct. You just have to create an equality comparer:
class VehicleEqualityComparer: EqualityComparer<Foo>
{
public override int GetHashCode(Foo f)
{
// A reasonably decent hash code for this application.
return (f.Make.GetHashCode() << 16)
| (f.Model.GetHashCode() >> 16)
^ f.Year;
}
public bool Equals(Foo x, Foo y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.Year == y.Year
&& x.Make == y.Make
&& x.Model == y.Model;
}
}
And to get a list of distinct items:
var returnVehicles = modelVehicles.Distinct(new VehicleEqualityComparer()).ToList();

Categories

Resources