How do I access the <List> inside another <List>? - c#

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.

Related

LiveCharts2 populating chart with Entity Framework

I'm starting with Livecharts2 (I did not try previous versions) and I'm not sure how to bind data with Entity Framework.
I need to display a chart with warehouse name and the quantity of access of each.
This is what I already have:
List<Access> access = context.Access.ToList();
var countAccess = access
.GroupBy(acc => acc.IdWarehouse)
.Select(group => new
{
Warehouse = group.Key,
Quantity = group.Count()
});
List<int> listQt = new List<int>();
List<int> list_warehouses = new();
foreach (var item in countAccess)
{
listQt.Add(item.Quantity);
list_warehouses.Add(item.Warehouse);
}
cartesianChart1.Series = new ISeries[]
{
new LineSeries<int>
{
Values = listQt,
Name = "Quantity"
},
new ColumnSeries<int>
{
Values = list_warehouses,
Name = "Warehouse"
}
};
My model classes:
public partial class Access
{
public int IdAccess { get; set; }
public byte IdWarehouse { get; set; }
}
public partial class Warehouse
{
public byte IdWarehouse { get; set; }
public string Name{ get; set; } = null!;
}
This code works with the IdWarehouse but Name, is there a better way to do it, instead of creating new list, foreach etc? I'm not sure if it is unnecessary
I tried with List<string> to get the warehouse's names, but got an exception cause is not implemented yet by creator/s

LINQ statement to get every recipe with list of ingridients

I have a question regarding the LINQ and foreach.
Lets say I have a simple DB with three tables (ingredient, recipe and many-to many relation ingredientinrecipe). They all have the simple structure of id and name.
I am running a console application where user could enter two ingredients for example
egg, flour
How would I structure the LINQ clause to go through all recipes where these both would be inside.
I am using UOW to structure everything together, so I could do uow.Ingridients.All ... etc etc
I figured I need to approach it from ingridientinrecipe table.
It would be something following:
var recipes = uow.ingridientinrecipe.All.Where(a => a.Ingridient.IngridientName.Contains( ...
But this works if and only if user enters one ingridient. How would I expand it to use two to infinite inputs.
I add what I have done so far.
var line = Console.ReadLine();
while (line.ToLower().Trim() != "exit")
{
var ingridients= line.Split(',');
Console.WriteLine("\nAre you looking for following receipes:");
foreach (var ingridient in ingridients)
{
var recipes= _uow.IngridientInRecipe.All.Where(a => a.Ingridient.IngridientName.Contains(ingridient)).ToList();
foreach (var recipe in recipes)
{
Console.WriteLine(recipe.Recipes.RecipeName);
}
Console.WriteLine();
line = Console.ReadLine();
}
TABLE STRUCTURE AS ASKED
public class Recipe
{
public int RecipeId{ get; set; }
public string RecipeName{ get; set; }
public virtual List<IngridientInRecipe> IngridientInRecipe{ get; set; }
}
public class Symptom
{
public int IngridientId{ get; set; }
public string IngridientName{ get; set; }
public virtual List<IngridientInRecipe> IngridientInRecipe{ get; set; }
}
public class IngridientInRecipe
{
public int IngridientInRecipeId{ get; set; }
public int RecipeId{ get; set; }
public virtual Recipe Recipe{ get; set; }
public int IngridientId{ get; set; }
public virtual Ingridient Ingridient{ get; set; }
}
You can use Intersect
var line = Console.ReadLine();
while (line.ToLower().Trim() != "exit")
{
var ingridients = line.Split(',');
Console.WriteLine("\nAre you looking for following receipes:");
var recipes = _uow.IngridientInRecipe.All;
foreach (var ingridient in ingridients)
{
recipes = recipes.Intersect(_uow.IngridientInRecipe.All.Where(a => a.Ingridient.IngridientName.Contains(ingridient)));
}
foreach (var recipe in recipes.ToList())
{
Console.WriteLine(recipe.Recipes.RecipeName);
}
Console.WriteLine();
line = Console.ReadLine();
}
You could use a PredicateBuilder from LinqKit (I have no affiliation with this):
var ingredients = ingredientList.ToLower().Split(new [] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);
var predicate = PredicateBuilder.False<Recipe>();
foreach (var ingredient in ingredients)
{
string temp = ingredient; // To avoid passing the loop variable into a closure
predicate = predicate.Or(r => r.Ingredients.Contains(temp);
}
reportQuery = reportQuery.Where(predicate);

How to create a dictionary of field values/counts from an observable collection?

I have an ObservableCollection<CustomerModel> Customers, that holds a Country field. What I want to do is, create an observable collection of type PiePointModel. In order to store the country name and number of occurrences of that country name.
So I set up an ObservableCollection<PiePointModel> CountryRatioCollection, where PiePoint holds a name and amount.
Then I tried to assign that collection to my Customers, by converting it to a dictionary holding the required values:
CountryRatioCollection = new ObservableCollection<PiePointModel>();
CountryRatioCollection = Customers.GroupBy(i => i.Country).ToDictionary(g => g.Key, g => g.Count());
But I get an error stating that this can't be implicitly converted:
Error 2 Cannot implicitly convert type 'System.Collections.Generic.Dictionary<string,int>' to 'System.Collections.ObjectModel.ObservableCollection<MongoDBApp.Models.PiePointModel>'
I understand that this is because the Dictionary type is not the same as my PiePoint model class.
Can anyone offer advice on making query and conversion?
This is the PiePoint class for reference, that holds the name and amount:
public class PiePointModel
{
public string Name { get; set; }
public int Amount { get; set; }
}
And this is the CustomerModel that holds the country field:
public class CustomerModel
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("firstName")]
public string FirstName { get; set; }
[BsonElement("lastName")]
public string LastName { get; set; }
[BsonElement("email")]
public string Email { get; set; }
[BsonElement("address")]
public string Address { get; set; }
[BsonElement("country")]
public string Country { get; set; }
public override string ToString()
{
return Country;
}
}
You should use Select (not ToDictionary) and create PiePointModel for each group.
IEnumerable<PiePointModel> piePoints = Customers.GroupBy(i => i.Country).Select(s => new PiePointModel()
{
Name = s.Key,
Amount = s.Count()
});
CountryRatioCollection = new ObservableCollection<PiePointModel>(piePoints);
Also notice that I used: CountryRatioCollection = new ObservableCollection<PiePointModel>(..) because CountryRatioCollection is of type ObservableCollection and you cannot assign here dictionary like in your example.
Constructor of ObservableCollection<T> can take IEnumerable<T> - I used it here.
Other way is use loop and add new PiePointModel to collection
CountryRatioCollection = new ObservableCollection<PiePointModel>();
var groups = Customers.GroupBy(i => i.Country);
foreach(var gr in groups)
{
PiePointModel piePointModel = new PiePointModel()
{
Name = gr.Key,
Amount = gr.Count()
};
CountryRatioCollection.Add(piePointModel);
}

Accessing element of a List. C#

I have created a List of type Students. The following classes are the ones I use;
public class StudentDetails
{
public class Address
{
public int HouseNumber{ get; set; }
public string LineOne{ get; set; }
public string LineTwo{ get; set; }
}
public class Student
{
public int StudentId { get; set; }
public Address StudentAddress{ get; set; }
}
public List<Student> GetStudents()
{
private Address StudentOne = new Address{//details};
private Address StudentTwo = new Address{//details};
var students = new List<Student>();
students.add(new Student {StudentId = 1, StudentAdress = StudentOne, //details});
//more students
return students;
}
}
Now I would like to access certain details of a particular student from this object. Say I want to get the House number of a student. How can i do that? I attempted to create another list, then add the list returned from GetStudents(). However when i iterate through it , i only get references to objects.
//To access the List
StudentDetails student = new StudentDetails(); //create new instance
for (int i = 0; i < student.GetStudents().Count; i++)
{
//Console.WriteLine(student[1].GetStudents());
}
You can use Linq to select the student you are searching for and then access its properties:
var student = GetStudents().FirstOrDefault(student => student.StudentId /* your student id here */>);
if (student != null)
{
var houseNumber = student.Address.HouseNumber;
}
Try this
StudentDetails student = new StudentDetails(); //create new instance
foreach (var s in student.GetStudents())
{
var id = s.StudentId;//use any properties using . operator
Console.WriteLine(s);
}
Console.WriteLine(GetStudents()[0].HouseNumber.ToString());
(0 can be whatever number)
or (gets a list of students and their house numbers) :
var houseNumbers = GetStudents().Select((student) =>
String.Format("Student: {0} - {1}",
student.StudentId, student.Address.HouseNumber));
foreach(var entry in houseNumbers) Console.WriteLine(entry);

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