How to cast while Sorting? - c#

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.

Related

How to get all records from class with a collection using lambda

how can I get a new list of object using lambda from the below scenario:
Example class:
AllCARS is a collection:
public class AllCars
{
int id {get; set;}
List cars {get; set;}
}
public class cars
{
string color;
string model;
}
I tried the following which didn't produce all rows:
var carRepo = new AllCars{};
var carsWithIds = carRepo.select(a => new {a.id, a.cars.color, a.cars.model})
Here is an example of my tables:
AllCars = id:123
cars[]
color model
red honda
blue toyota
How can i write the lambda function to produce a list like this
123 red honda
123 blue toyota
If I understand the question right, you cannot perform a linq request to an object with collection. However, you can query the collection inside this object to produce the required output. Hope the following code snippet would help:
var carsWithIds = carRepo.cars.Select(a => new {id = carRepo.id, a.color, a.model });
This code allows you to get a new object of anonymous class, that contains fields: id, color and model.
There is a little inaccuracy in your code example. Do not forget to make properties of AllCars and cars classes public; otherwise they won't be accessible.
Let AllCarsList be the collection of AllCars then you can make use of extension method .SelectMany to get the resultCollection. Please go through the following lines of code and the example and please let me know if it helps as well as for more clarifications.
var resltCollection = AllCarsList.SelectMany(p => p.cars,(parent, child) => new
{
ID = parent.id,
Color = child.color,
Model = child.model
}).ToList();
Here is a working Example

How to TypeCast SelectMany Results

I'm trying to flatten a list-of-lists, and at the same time ensure that the list of final objects is of the correct type. Here's an (abstract) example:
class Space
{
public List<Space> ContainedSpaces
{
get;
set;
}
}
class Library : Space
{
public void AddRoom(Room room)
{
ContainedSpaces.Add(room);
}
}
class Room : Space
{
}
class Test
{
public List<Room> ListOfRooms(List<Library> libraries)
{
return libraries.SelectMany(lib => lib.ContainedSpaces).ToList();
}
}
My error in this example, in ListOfRooms, is:
Cannot implicitly convert type 'System.Collections.Generic.List<SoftTech.Integration.Space>' to 'System.Collections.Generic.List<SoftTech.Integration.Room>'
Without arguing about the class design (it's an abstract example), how do I get SelectMany to typecast each item in the final list to a Room object?
return libraries.SelectMany(lib => lib.ContainedSpaces).OfType<Room>().ToList();
how do I get SelectMany to typecast each item in the final list to a Room object
Well, you can't safely cast because not every Space is a Room. Technically a Library could contain other types of Spaces (even though you only define two).
In your design this would be perfectly legal:
Library l = new Library();
l.ContainedSpaces = new List<Space>();
l.ContainedSpaces.Add(new Library());
You can try to cast using:
libraries.SelectMany(lib => lib.ContainedSpaces).Cast<Room>().ToList();
but if some of the Spaces are not Rooms then you'll gat an InvalidCastException.
Or if you want just the spaces that are rooms then you can use OfType as suggested earlier:
libraries.SelectMany(lib => lib.ContainedSpaces).OfType<Room>().ToList();

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

Use inherit class for instance List in C#

Hello stackoverflow community!
Let's start!
I have one small class Person:
class Person
{
public string name { get; set; }
}
Аnd it has a descendant Employee:
class Employee : Person
{
public int salary { get; set; }
}
And second descendant is Guest:
class Guest: Person
{
public int id { get; set; }
}
Ok! looks good :)
Now I want to display a list of all employees OR guests in a single control ListView
I made a class (it really necessary) for list management PeopleList:
class PeopleList
{
public List<Person> People { get; set; }
...
public void LoadListFromFile()
{
// Load logic
this.People = new List<?>(source);
}
}
Do you see this question mark? No? Look at the code again!
How to create an instance of List that I can use my class something like this:
// This instance with the list of Person objects
PeopleList list = new PeopleList();
foreach (Employee e in list.People)
{
Debug.WriteLine(e.salary.toString());
}
// This instance with the list of Guest objects
PeopleList list = new PeopleList();
foreach (Guest g in list.People)
{
Debug.WriteLine(g.id.toString());
}
P.S. I'm new in c# and I think that I have a problem in architecture. And maybe you point me to the pattern solves my problem. I really need your help! Thank you!
I think you're after OfType, in the System.Linq library:
foreach (Employee e in personList.OfType<Employee>())
{
Debug.WriteLine(e.salary.toString());
}
foreach (Guest g in personList.OfType<Guest>())
{
Debug.WriteLine(g.id.toString());
}
The only field in Person that is shared with all of the decedents is the field 'name', therefore, if you are casting each item in your list to Person you will only have access to 'name'.
You have a few options to solve this issue. 1) you could move all of the common fields into the base class, but this is probably not want since it defeats the point of an object hierarchy, or 2) you can type check, using the "Is" keyword, each person in the list so see what type of person it is and then cast that Person to the appropriate decedent class before you operate on it.
For example:
foreach (Person p in list.People)
{
if(p is Employee)
{
Debug.WriteLine(((Employee)p).salary.toString());
}
if(p is Guest)
{
Debug.WriteLine(((Guest)p).Id.toString());
}
}
Here is an alternate more clear way of casting
foreach (Person p in list.People)
{
if(p is Employee)
{
Employee employee = p as Employee;
Debug.WriteLine(employee.salary.toString());
}
if(p is Guest)
{
Guest guest = p as Guest;
Debug.WriteLine(guest.Id.toString());
}
}
Additionally you can learn more about type checking using the "is" keyword here.
Also just for clarity, since it might not be obvious, some others have suggested that you use OfType in linq but this more of a way to filter like object from a list of mixed objects as opposed to actually type checking each one.
Enjoy!
As the comments are saying, you can do the following;
public void LoadListFromFile()
{
// Load logic
this.People = new List<Person>(source);
}
And then, cast where appropriate;
foreach (Person e in list.People.Where(p => p.GetType() == typeof(Employee)))
{
Debug.WriteLine(((Employee)e).salary.toString());
}
I think in your particular example, the inheritance structure is not adding much benefit, but I digress.
In your example, the easiest solution would be to override the ToString method in each one of your classes.
Having to know the exact type of the object before hand before you run some sort of calculation or display puts the onus of that calculation onto the calling code, if you make a 3rd type, you would have to update all possible references, giving you a maintenance nightmare.
The feel the logic of how to represent itself should be the responsibility (in your example) of the object itself.
This is what overriding ToString gives you, the responsibility of representing itself is then given to the thing that knows best (the object you're asking).

Sorting out list with several properties

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

Categories