Retrieve data from the nested list - c#

How can I get all data from a nested list in C#?
Each person can have multiple cars.
Now I have a list of people and each item has a list of cars.
public class Person
{
public string FullName { get; set; }
public List<Car> Cars { get; set; }
}
public class Car
{
public string Name { get; set; }
}
And values:
var people = new List<Person>()
{
new Person()
{
FullName = "Jack Lee",
Cars = new List<Car>()
{
new Car()
{
Name = "BMW"
},
new Car()
{
Name = "Tesla"
}
}
},
new Person
{
FullName = "Micheal doug",
Cars = new List<Car>()
{
new Car()
{
Name = "Ferrari"
}
}
}
};
What is the best way to get all car names with one query?
Is it possible to get all cars with one query?

With System.Linq, you can use .SelectMany() to flatten the Cars lists from the people list and get the Name.
using System.Linq;
var result = people.SelectMany(x => x.Cars)
.Select(x => x.Name)
.ToList();
Demo # .NET Fiddle

Related

Add element to a List when delcaring new object

Is there a way to add elements to a List when doing this:
var Foo = new MyClass() {
PropertyList = MyList,
Id = Id,
}
I would like to add elements to PropertyList. For example would be the same as: MyList.Add()
The problem is that i do not have a list called MyList but i rather have elements that i want to append to PropertyList
Updating code based on comments:
var result1 = await query
.GroupBy(c => new {
c.CommissionId, c.ActivityId
})
.Select(grp => new RegistrationStatisticViewModel() {
CommissionId = grp.Key.CommissionId,
CommissionCode = grp.First().Commission.Code,
CommissionDescription = grp.First().Commission.Description,
MinuteWorked = grp.Sum(c => c.MinuteWorked),
ActivityId = grp.Key.ActivityId,
ActivityCode = grp.First().Activity.Code,
ActivityDescription = grp.First().Activity.Description,
})
.ToListAsync();
var grps = from d in result1
group d by d.CommissionId
into grp
select new RegistrationStatisticViewModel() {
CommissionId = grp.Key,
ActivityList = new List < Activity > {
new Activity {
//ActivityId = grp.Select(d => d.ActivityId),
//Code = grp.Select(d => d.ActivityCode),
//Description = grp.Select(d => d.ActivityDescription),
}
},
CommissionCode = grp.First().CommissionCode,
CommissionDescription = grp.First().CommissionDescription,
MinuteWorked = grp.First().MinuteWorked
};
return grps;
To give context:
forget the result1 is just some data i retrieve from my database
Commission is one class and contains:
CommissionId
Code
Description
Activity is one class and contains:
ActivityId ==> type GUID
Code ==> type string
Description ==> type string
Now the var = grps is a LINQ that gets the data and then instatiates a new object (class) new RegistrationStatisticViewModel()
So the tricky part we were discussing before is when i populate ActivityList with multiple activities.
When populating the list if i use .First() or .Select() i would only get one instance and therfore the list would only have one activity.
It worked when using .ToArray() for example if i replace ActivityList with just the ActivityId of type string (so a new property on RegistrationStatisticViewModel that is not a list anymore):
I can do this ActivityId = grp.Select(d2 => d2.ActivityId).ToArray()
And it will give me an array of all the ActivityId linked to that commissionId
I am sorry if this is confusing but it is for me as well. I would thank you if you could help me. No worries if you can't you have already give me very helpful answers, so i thank you for that!
Based on your remarks, I believe this is what you are trying to achieve:
public class PersonContainer
{
public IList<Person> Persons { get; set; }
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var personContainer = new PersonContainer
{
Persons = new List<Person>
{
new Person
{
Name = "John Doe",
Age = 28,
},
new Person
{
Name = "Jane Doe",
Age = 27,
},
}
};
Here, the Persons property of PersonContainer is initialized and populated with Person elements during instantiation.

Nest foreach cycle and adding items to collection into linq methods

I have the following classes:
class Person
{
public string Name { get; set; }
}
class Student
{
public string Name { get; set; }
}
I have a list of persons:
IList<Person> persons = new List<Person>
{
new Person() { Name = "Bill" },
new Person() { Name = "Bob" },
new Person() { Name = "Henry" },
};
And I adding items to new collection in foreach loop:
IList<Student> students = new List<Student>();
//Is it possible to nest the following rows in linq method?
foreach (var person in persons)
{
students.Add(new Student() { Name = person.Name });
}
Is it possible to nest foreach and adding items into linq method?
If you mean replacing the foreach loop with a LINQ query (not quite sure why you are talking about nesting loops in your question) then you could try this:
IList<Student> students = persons.Select(p => new Student { Name = p.Name }).ToList();
or if you prefer:
IList<Student> students = (from p in persons select new Student { Name = p.Name }).ToList();

How to access the Inner Proprty at the time of new object creation using LINQ C#?

I'm having a model class name person
public class person
{
public string Name { get; set; }
public string[] cars { get; set; }
public int NoOfCars { get; set; }
}
I'm having an ObservableCollection namely PersonsList. Now I'm going to initialize the ObservableCollection
person pItem = new person
{
Name = "Sakthi",
cars = new string[] { "Honda City", "Maruthi Ciaz" },
NoOfCars = cars.Count()
}
ObservableCollection<person> PersonsList = new ObservableCollection<person>();
PersonsList.Add(pItem);
Here I need to assign the NoOfCars from the cars Property without mentioning manually the value as 2 or by using external string[] variable explicitly. I need the Solution implicitly inline by using LINQ C#
You can add the logic to the property itself:
public class Person
{
public string Name { get; set; }
public string[] Cars { get; set; }
public int NoOfCars { get { return Cars == null ? 0 : Cars.Length; } }
}
No need to provide a setter.
Side note: you should follow the .NET Capitalization Conventions.
If you can't modify the class and you need it inline(why at all?), you can't access other properties from within the object initializer since the object is yet not fully initialized. So it's simply not allowed.
Why can't you store the array in a variable?
string[] myCars= { "Honda City", "Maruthi Ciaz" };
person pItem = new person
{
Name = "Sakthi",
cars = myCars,
NoOfCars = myCars.Length
}
Field-initialization is a all-or-nothing-operation. Either all operations or none are executed, however the results of this embracing operations are only visible when everything is done. Thus you cannot use cars.Count() within the initializer. In particular the cars-property does not even exist in the scope of your initializer so you canĀ“t access it in any way.
You have to set it after initializing your Person either explicitely via NoOfCars = pItem.cars.Count() or as Tim already mentioned within the getter of NoOfCars itself.
EDIT: If you really want to use field-initialization you may create the array in front and set the Person afterwards:
var cars = new string[] { "Honda City", "Maruthi Ciaz" };
person pItem = new person
{
Name = "Sakthi",
cars = cars,
NoOfCars = cars.Count()
}
If your model class is remote please try this
person pItem = new person
{
Name = "Sakthi",
cars = new string[] { "Honda City", "Maruthi Ciaz" },
NoOfCars = 0
}
and before adding the item to list compute the value of NoOfCars
ObservableCollection<person> PersonsList = new ObservableCollection<person>();
pItem.NoOfCars = pItem.cars.Count();
PersonsList.Add(pItem);
If you can't modify the Person class, you can write a method (otherwise you could give it a proper constructor):
Person CreatePerson(string name, string[] cars) {
Person p = new Person();
p.Name = name;
p.Cars = cars;
p.NoOfCars = cars.Length;
return p;
}
Then call it:
PersonsList.Add(CreatePerson("Sakthi", new string[] { "Honda City", "Maruthi Ciaz" }));

How do I access the <List> inside another <List>?

I'm having a tough time figureing out how to use the ForEach (or not use it) to print out the Car list I have inside the Parent list. I know obviously I can't do XmlData.p.b.ForEach and that the code below is just an example, as I'm going to have to do a lot of manipulation, but I just need to know how I can access the list in b inside the list p.
public static void Main(string[] args)
{
XmlSerializer deserializer = new XmlSerializer(typeof(Address));
TextReader reader = new StreamReader("myXML.xml");
object obj = deserializer.Deserialize(reader);
Address XmlData = (Address)obj;
reader.Close();
XmlData.p.ForEach(p => Console.WriteLine("Item in p!");
XmlData.p.b.ForEach(b => Console.WriteLine("Item in b!"); // can't do this
}
XML Format:
Top: MyRoot
----------Target
-----------------Parent
----------------------------Cars
[XmlRoot("MyRoot")]
public class MyRoot
{
[XmlElement("Dealership")]
public String dealership;
[XmlElement("ZipCode")]
public String zipCode;
[XmlArray("Targets")]
[XmlArrayItem("Parent")]
public List<Parent> p = new List<Parent>();
}
public class Parent : Student
{
[XmlElement("ParentName")]
public String parentName { get; set; }
}
public class Student
{
[XmlElement("Owner")]
public String owner { get; set; }
[XmlElement("Age")]
public String age { get; set; }
[XmlElement("Name")]
[XmlArrayItem("Cars")]
public List<Cars> c = new List<Cars>();
}
public class Cars
{
[XmlElement(Namespace="BuildDependency/CarYear")]
public String carYear { get; set; }
[XmlElement(Namespace = "BuildDependency/CarMake")]
public String carMake { get; set; }
[XmlElement(Namespace = "BuildDependency/CarModel")]
public String carModel { get; set; }
[XmlElement("CarColor")]
public String carColor { get; set; }
[XmlElement("CarMileage")]
public String carMileage { get; set; }
}
You could flatten the list using SelectMany:
XmlData.p.SelectMany(p => p.b).ToList()
.ForEach(b => Console.WriteLine("Item in b!"));
If you wish to iterate a list that is inside another list easy, you can do it within the first list iteration scope, something like (not gonna use lambda expressions but standard loops to keep it simple):
for(int i=0; i<XmlData.p.Length; i++) {
Console.WriteLine("Item in a!");
for(j=0; j<XmlData.p[i].b.Length; j++) {
Console.WriteLine("Item in b!");
}
}
Or you can just nest the second ForEach call:
XmlData.p.ForEach(outer => outer.b.ForEach(inner => Console.WriteLine("Item in b!")));
It's quite obvious that this doens't work, your code needs to be adapted so the b come's into the foreach class.
Your 2 nd call (iterating over list in list) should be:
XmlData.p.ForEach(p => p.c.ForEach(c => { Console.WriteLine("Item in C"); }));
I've written a very small demo project that I will share:
First some classes to make the project work:
public class MyRoot
{
public List<Student> p = new List<Student>();
}
public class Student
{
public List<Cars> c = new List<Cars>();
}
public class Cars
{
public String carYear { get; set; }
public String carMake { get; set; }
public String carModel { get; set; }
public String carColor { get; set; }
public String carMileage { get; set; }
}
Then the initiazization:
var cars = new List<Cars>();
cars.Add(new Cars());
cars.Add(new Cars());
cars.Add(new Cars());
cars.Add(new Cars());
var student = new List<Student>();
student.Add(new Student() { c = cars });
var root = new MyRoot();
root.p = student;
So we create 4 cars and 1 student with the 4 cars.
In the root object we set the list of students to just 1 student with the 4 cars.
So we have the following situation:
-- Root
-- Student
-- Car
-- Car
-- Car
-- Car
Now, to iterate over it, you need to do the following:
root.p.ForEach(p => Console.WriteLine("Item in 1"));
root.p.ForEach(p => p.c.ForEach(c => Console.WriteLine(" Item in 2")));
This will give the output:
Note: When you have 2 students, 1 student with 4 cars and the other one with 2 cars, the output will become:
This is because first we're iterating over the students and then over the cars.
When you see for each student it's cars, you can nest your foreach. Your foreach will then looks like:
root.p.ForEach(p =>
{
Console.WriteLine("Item in 1");
p.c.ForEach(c => Console.WriteLine(" Item in 2"));
});
And then your output will be:
So, fairly long post but I hope it helps.

Custom OrderBy on a List of List

I want to orderby on a List like:
public List<Person> personList { get; set; };
class Person
{
public string Name{ get; set; };
public List<Car> carList{ get; set; };
}
class Car
{
public string Name { get; set; };
public int PriceInMillions { get; set; };
}
I want to sort CarList of each PersonList such that CarList has BMW object(can be one or more) at top of list?
Input:
Person: Name: 'ABC'
List<Car>:{{"x",25},{"y",25},{"BMW",26},{"x",25},{"BMW",25}}
Person: Name: 'DEF'
List<Car>:{{"BMW",21},{"y",25},{"BMW",26},{"x",25},{"BMW",20}}
Required Output:
Person: Name: 'ABC'
List<Car>:{{"BMW",25},{"BMW",26},{"x",25},{"y",25},{"x",25}}
Person: Name: 'DEF'
List<Car>:{{"BMW",20},{"BMW",21},{"BMW",26},{"y",25},{"x",25}}
I tried to find the index of "BMW" and swap it with first index but it is not working.
List<Car> orderedCarList = person.carList.OrderBy(s => (s.Name == "BMW") ? 0 : 1).ToList();
person.carList = orderedCarList;
If you want to sort in-place, without creating a new list or cars for each person, you can do
foreach(Person p in personList)
{
p.carList.Sort((car1, car2) =>
-(car1.Name == "BMW").CompareTo(car2.name == "BMW"));
}
Here is working solution:
public List<Person> GetSortedList(List<Person> personList)
{
foreach (var person in personList)
{
person.CarList=person.CarList.OrderByDescending(x => x.Name== "BMW").ToList();
}
return personList;
}

Categories