Sorting out list with several properties - c#

I am pretty confused with sorting out list in C#, I have gone through different tutorials for sorting e.g. sorting using list (sort method) or use IComparable but nothing actually helped me with what I want.
for example I have a list of cars,
Car Number
Car Title
and few other properties
Now Car number can be any double number or it can be Nan (null) and Car Title can never be null.
I want to sort list in a way that first it gets sorted by Car Numbers and if they are not available for a car then use its title to sort rest of cars.
so if I have something like this,
Car 1 100.0 (car number) BMW x6 (car name)
Car 2 (there isn't any car number) Mercedies A class (car name)
Car 3 99.0 Alpha Romeo
Car 4 1.2 Jeep
Car 5 4.1 Victoria Marshall 1933
Car 6 no number Vauxhal
Out put I want my list class to be sorted as
Car 4, Car 5, Car 3, Car 1, Car 2, Car 6
Thanks for help.

try something like
var lst = new List<Cars>()
//add cars to list
lst = lst.Orderby(c => c.CarNumber).ToList();
you can extend it like this:
lst = lst.Orderby(c => c.CarNumber).ThenBy(c=> c.Title).ToList();
Or
You implement the IComparable so it will sort the way you want:
public class Car : IComparable<Car>
{
public string Number { get; set; }
public int CompareTo(Car obj)
{
return obj.Number.CompareTo(Number);
}
}

Related

Ordering List<string> that contains keywords from a template c#

I have a problem. I need to order a C# List<string> based on a template, but the template contains only a part of the string, so it must be Contains().
The template would be something like:
House
Vehicle
Electric
and the input something like:
Electric bike
Household appliances
Electricity
Electric generator
Vehicle appliances
and the ordered list should be like:
Household appliances
Vehicle appliances
Electric bike
Electricity
Electric generator
Is there anything that can help me?
You must implement custom comparer
public class CustomStringComparer : IComparer<string>
{
List<string> templates = new List<string> { "House", "Vehicle", "Electric" };
public int Compare(string x, string y)
{
string xTemplate = templates.FirstOrDefault(t => x.Contains(t));
string yTemplate = templates.FirstOrDefault(t => y.Contains(t));
int xTemplateIndex = templates.IndexOf(xTemplate);
int yTemplateIndex = templates.IndexOf(yTemplate);
return xTemplateIndex.CompareTo(yTemplateIndex);
}
}
And then you can pass this comparer to Sort()
List<string> input = new List<string>
{
"Electric bike",
"Household appliances",
"Electricity",
"Electric generator",
"Vehicle appliances"
};
input.Sort(new CustomStringComparer());

LINQ grouping by child properties

I'm using the GroupByMany extension method illustrated here:
http://blogs.msdn.com/b/mitsu/archive/2007/12/22/playing-with-linq-grouping-groupbymany.aspx
This is working great, except I'm trying to group based on a list of entity properties.
An example of this would be a Car class that has a set of properties:
public class Car
{
private string Model {get; set;}
private int Year {get; set;}
private List<CarProperty> Properties { get; set; }
}
public class CarProperty
{
private string Name { get; set; }
private string Value { get; set; }
}
If I have a list of Car objects and the respective properties. The main issue is that the class can contain multiple properties with the same name:
Car1 : {"Colour", "Red"}, {"Colour", "Blue"}
Car2 : {"Colour", "Blue"}
Car3 : {"Colour", "Black"}, {"Colour", "Red"}
Car4 : {"Colour", "Yellow"}
I can create group selectors to group by 'Year' or 'Model' very easily:
c => c.Model;
c => c.Year;
The problem is I'm trying to create a group selector for one of the properties.
I have tried this (to group by car colour):
c => c.Properties.Where(p => p.Name == "Colour").Select(p => p.Value));
But it's create a composite grouping key. I somehow need to flatten the list of possible grouping values to give:
Red: Car1, Car3
Blue: Car1, Car2
Black: Car3
Yellow: Car4
EDIT:
If the GroupByMany isn't the way to go, I'd appreciate any suggestions to take a list of groupings like {"Year", "Model", "Colour"} which will multi-level group the cars in the same order as the list of groups.
Year
--> Model
--> Colour
It seems I was making the whole thing more complicated than necessary. This answer helped me find a solution:
LINQ: grouping based on property in sublist

How to check a list for a different instance of a class inheriting generic interface

I have a List<ICar<T>> cars. I want to add new ICar<T> Honda to the list only if it does not already exist in there. The structure is like:
class Honda : IHonda{}
interface IHonda : ICar<T>
class Ford : IFord{}
interface IFord : ICar<T>
I only want to put one of each type of car into the list, but only knowing that they are ICar<T>. I don't know ahead of time the list of possible cars. How can this be done?
I was thinking something like
cars.Any(i => i.GetType().GetInterfaces().Contains(
car
.GetType()
.GetInterfaces()
.Where(s =>!cars.GetType().GenericTypeArguments != s.GenericTypeArguments
))))
However, something is wrong with that and I can't quite see a path through this one.
If you have a Type instance corresponding to T then you can use:
Type t = //
Type carType = typeof(ICar<>).MakeGenericType(t);
bool exists = cars.Any(c => carType.IsAssignableFrom(c.GetType()));
I think that you can use
//get list of all interfaces in the currrent car list
//you will have to check the contents of yourList as it may contain some types
//such as IList / IEnumerable that you will want to filter out.
var yourList = list.SelectMany(car => car.GetType().GetInterfaces());
//check get all interfaces on your car and check if the list contains it
bool exists = yourCar.GetType().GetInterfaces().Any(c => yourList.Contains(c));
You may be able to compare the elements using the fully qualified name of the type.
CarTypeComparer compareCarTypes = new CarTypeComparer();
if (!List.contains(car, compareCarTypes))
{
// insert car into list
}
class CarTypeComparer : IEqualityComparer<ICar<T>>
{
public bool Equals(ICar<T> car1, ICar<T> car2)
{
if (car1.GetType().FullName == car2.GetType().Fullname)
{
return true;
}
else
{
return false;
}
}
// implement GetHashCode() ....
}
Add a CarModel property inside of your ICar interface,and then implement it in your classes.For example in Honda class:
public string CarModel { get { return "Honda"; } }
you can check CarModel property in your query and ofcourse you can group your cars by CarModel,i hope this helps..

How can I prevent changing an object by changing another?

I understand that these two objects point to the same reference, but I don't want that to be the case, so I am a bit confused on how to prevent one from changing the other. Do I need to declare a band new Car object, for example Car car2 = new Car();? When I do this resharper tells me it is unnecessary. Here is my code:
void Main()
{
Car car = new Car { Color = "Blue"};
Console.WriteLine(car.Color);
//Do I need Car car2 = new Car();
//car2 = car; //Confused.
Car car2 = car;
Console.WriteLine(car2.Color);
car = Format(car);
Console.WriteLine(car.Color);
Console.WriteLine(car2.Color); //How can I prevent car2 from changing color?
}
// Define other methods and classes here
public class Car
{
public string Color {get;set;}
}
public static Car Format(Car car)
{
car.Color = "Red";
return car;
}
var car2= new Car();
car2 = car;
car = Format(car); //This changes car and car2
When I do this resharper tells me it is unnecessar
Then Resharper would be wrong in this case. But it depends on your exact code.
When you want car1 and car2 to be different colors then they should be different instances. And that does require Car car2 = new Car();
Car is a class is a reference-type.
In your posted code there is only 1 instance, so there can only be one color.
Note that for the same reason your Format method does not need to return anything, the following will do exactly the same:
public static void Format(Car car)
{
car.Color = "Red";
}
As you mention, car2 and car refer to the same object. It sounds as if you don't want that, so you will need to create a copy of car and call it car2.
You could define a copy constructor for your Car class:
public Car(Car car)
{
this.Color = car.Color;
}
Then use it like this:
Car car2 = new Car(car);
You can make Car a struct
public struct Car
{
public string Color { get; set; }
}
or make Car ICloneable or any other of the dozen ways to copy the object so you can easily have another instance. We need to see the code that shows the warning so we could figure out the problem. I'm not seeing the warning.

How to cast while Sorting?

I have classes Vehicle and class:Car is inherited from class:Vehicle as in below. I have a List which I want to sort using LINQ based on a property of Car class (not of vehicle parent class).
class vehicle
{
public String Name {get; set; }
}
class Car:Vehicle
{
public String ModelName {get; set; }
}
List<Vehicle> vehicleList=new List<Vehicle>();
Car c1 = new Car();
vechicleList.Add(c1); //s note that i am adding **Car** objects
Car c2 = new Car();
vechicleList.Add(c2);
// added 10 such Car objects
Then I want to Sort vehicleList based on CarModel (which is a property of Car, not of Parent class)
I tried the below one but it does not work.
vehicleList.OrderBy(c => (Car)c.ModelName)
Any help on how to do this?
You have to decide what you want to have:
A list of Car instances or a list of Vehicle instances.
If you want to have a list of Vehicle instances you can't order by properties of Car, because in a list of Vehicles there also could be a Bus or a Bicycle.
Doing what you currently try (casting to Car) will possibly throw an exception at runtime.
Having said that, if you insist on doing it, you have to be aware of two things:
You need to fix your cast: vehicleList.OrderBy(c => ((Car)c).ModelName)
You need to be aware that OrderBy doesn't perform an in-place sort. vehicleList will still be in its original ordering. If you want to have the ordered result in vehicleList, you need to assign the result of OrderBy to it:
vehicleList = vehicleList.OrderBy(c => ((Car)c).ModelName).ToList();
The most readable code that does what you want is
vehicleList.Cast<Car>().OrderBy(c => c.ModelName);
while of course you could also fix the parens and write the same as
vehicleList.OrderBy(c => ((Car)c).ModelName);
That said, both of the above will blow up if there is any Vehicle in your list that is not a Car. And if that can't happen, then why isn't that a List<Car> in the first place?
You have to cast c into a Car, not c.ModelName.
Try :
vehicleList.OrderBy(c => ((Car)c).ModelName)
try use:
vehicleList.OfType<Car>().OrderBy(c=>c.ModelName)
note:
this returns only cars from yours list
vehicleList.OfType<Car>().OrderBy(c=>c.ModelName)
Try this:
vehicleList.OrderBy(c=>((Car)c).ModelName)
but beware it will blow up if an item is not actually a car!
A way around that would be:
vehicleList.OrderBy<Vehicle, string>(c=>
{
Car car = c as Car;
if (car != null)
return car.ModelName
else
return "";
}
But if there were 'non-cars' there position would be determined by the empty string in relation to other items in the list.

Categories