Child List from Parent List parameter without duplicates C# VS2013 - c#

I am working on a small expense tracking program. The idea is to have a list that holds Expense objects that can be manipulated and used to perform calculations.
I was able to create the List without issue and populate it with several dummy expenses. My expenses are grouped by category, Expense.expenseType, to allow me to do calculations for analysis so I am trying to make another List that will store category names and relevant calculations values. The list of category names is meant to remove duplicates but so far I've been unsuccessful at populating it.
My approach for creating the List has been to define a Category class that holds only a string parameter for categoryName and a float for categoryTotal although the latter is initialized to 0.00. I then have a For loop that copies the names into the List and a second For loop that removes indexes based on the name once they've been alphabetized. I've tried different variations of this but ultimately I get either an index that is out of bounds or a reduced but still duplicates list of categoryName.
Really hoping to get some advice so I could move forward with the code. I didn't add the actual code since I'm new to C#/VS and figure I may be approaching the problem all wrong.
Edit 1: Based on the feedback I got, the function I am using is below:
public void getCategories(List<Category> passedCategories)
{
passedCategories = passedCategories.GroupBy(Category =>Category.strName)
.Select(gr => new Category
{
strName = gr.Key,
fltTotal = gr.Sum(ex => ex.Value)
});
}
This function is not working, I have a few points I wanted to clarify and I am sure there are others I missed.
Passed categories is a List of Categories that have three parameters - strName, fltTotal and fltPercent. The latter two are currently set to zero when the whole list is populated via a temp Category. The strName is being copied from an Expense List with many more parameters. Since the Category name will repeat in the Expense List, I am trying to remove all duplicates so I can have just the different categories. I took out var since I am passing the List in, should I not have done this? What am I missing?
Thanks again for the help,
Yusif Nurizade

That you need is something like the following. I say something, because I don't see your code and I have to imagine it. For instance, I don't know the name of the property for the amount of expense. I assumed that this is called Value.
// This would be the list of expenses. You have to populate it with data.
var expenses = new List<Expense>();
// Using LINQ you can achieve that you want in a few lines.
// First you group by your data by their categories.
// Then you calculate the total expense for each category.
var statistics = expenses.GroupBy(expense=>expsense.Type)
.Select(gr=> new Category
{
Name = gr.Key,
Total = gr.Sum(ex=>ex.Value)
});

Related

Creating a linq condition using a list

I have a method which used for getting a list from the database.
public List<SelectedCustomers> GetCustomers(List<int> customerNumbers)
{
var customers=_context.Customers.Where(?).Select(i=> new SelectedCustomers() {}).ToList()
}
I want to retrieve information from the database of customers whose customer number is given by the user. There are about one hundred thousand customers in the customer list in the database. I do not want the method to take the whole list and search it every time it is called, it takes too much effort. However, I don't know how to use a list in where ().
In summary, instead of pulling out all the list I want and searching the values requested by the user in the list, I want to go to the database with the list that comes directly from the user and give me the information of these customers.
I hope I could explain. Thank you for your help.
Try this:
var customers = _context.Customers.Where(c => customerNumber.Contains(c.CustomerId)).Select(i => new SelectedCustomers() { }).ToList()
Try something like (not tested)
public List<SelectedCustomers> GetCustomers(List<int> customerNumbers)
{
var customers=_context.Customers.Where(x =customerNumbers.Contains(x.customerNumber)
.Select(i=> new SelectedCustomers() {}).ToList()
}
This is the equivalent of the SQL IN ()

How do I obtain all the most recent elements from an IEnumerable?

I'm developing a Windows Forms application project for my university and we are using Entity Framework to store things.
It's an e-commerce type of program and now I'm struggling to find the right way to filter an IEnumerable based on the most recent ones.
What I want is to obtain all the elements from this table called prices, in which we also store older prices as a history backup.
This table has the ID of the article that refers to, the same for the corresponding prices list, a public, and a cost price, the updated date that is the moment it was created/updated.
I have tried using many expressions but ultimately failed miserably, sometimes I brought me only the ones within a certain price list or none at all or just one.
Again, I need it to work for a function that lets you update your prices based on parameters. For example, all articles and all price lists. For that, I want only the ones that are up to date so I won't touch the history of prices.
Example of what it should return:
Thank you very much!
Update: What I have tried didn't work, in fact, I couldn't even find sense in the code I wrote, that's why I didn't post it in the first place. I guess this problem ended my brain and I can't think properly anymore.
I tried some answers that I found here. For example:
// This is an IEnumerable of the price DTO class, which has the same properties as the table.
// It contains all the prices without a filter.
var prices= _priceService.Get();
// Attempt 1
var uptodatePrices= prices.GroupBy(x => x.ArticleId)
.Select(x => x.OrderByDescending(s => s.Date).FirstOrDefault());
// Attempt 2
uptodatePrices = prices.Select(x => new PriceDto
{
Date = prices.Where(z=> z.Id == x.Id).Max(g=>g.Date)
});
Ok, It sounds like you want to return the latest price for a combination of price list and article..
You're on the right path with your first attempt, but not quite there. The second attempt looks like pure frustration. :)
I believe the solution you will be looking for will be to group the products, then take the latest price for each group. To do that you need to use the values that identify your group as the group by expression, then sort the grouped results to take your desired one.
var uptodatePrices= prices.GroupBy(x => new { x.ArticloId, x.ListPrecioId} )
.Select(x => x.OrderByDescending(p => p.Date).First())
.ToList();
When you do a GroupBy, the value(s) you specify in the groupby expression become the "Key" of the result. The result also contains an IEnumerable representing the items from the original expression set (prices) that fit that group.
This selects the Price entity, you can change the Select to select a DTO/ViewModel to return, populated by the price instead as well.
In your case you were grouping by just the ArticloId, so you'd get back the latest entry for that Article, but not the combination of article and list price. In the above example I group by both article and list price, then tell it to Select from each group's set, take the latest Price record. I use First rather than FirstOrDefault as because I am grouping on combinations I know there will be at least 1 entry for each combination. (or else there would be no combination) Avoid using ...OrDefault unless you're sure, and are handling that no result may come back.
What you are working with are LINQ queries. If you only need to sort by most recent date, you can do that like this:
prices.OrderByDescending(price=>price.FechaActualizacion).ToList();
Make sure your Price model has the FechaActualizacion property.

Fastest way to filter two lists based on a common field datapoint in 1-many configuration

This one is all about performance. I have two major lists of objects (here, I'll use PEOPLE/PERSON as the stand-in). First, I need to filter one list by the First_Name property - then I need to create two filtered lists from each master list based on shared date - one list with only one name, the other list with every name, but with both lists only containing matching date entries (no date in one list that doesn't exist in the other). I've written a pseudo-code to simplify the issue to the core question below. Please understand while reading that BIRTHDAY wasn't the best choice, as there are multiple date entries per person. So please pretend that each person has about 5,000 "birthdays" when reading the code below:
public class Person
{
public string first_Name;
public string last_Name;
public DateTime birthday;
}
public class filter_People
{
List<Person> Group_1 = new List<Person>();// filled from DB Table "1982 Graduates" Group_1 contains all names and all dates
List<Person> Group_2 = new List<Person>();// filled from DB Table "1983 Graduates" Group_2 contains all names and all dates
public void filter(List<Person> group_One, List<Person> group_Two)
{
Group_1 = group_One;
Group_2 = group_Two;
//create a list of distinct first names from Group_1
List<string> distinct_Group_1_Name = Group_1.Select(p => p.first_Name).Distinct().ToList();
//Compare each first name in Group_1 to EVERY first name in Group 2, using only records with matching birthdays
Parallel.For(0, distinct_Group_1_Name.Count, dI => {
//Step 1 - create a list of person out of group_1 that match the first name being iterated
List<Person> first_Name_List_1 = Group_1.Where(m => m.first_Name == distinct_Group_1_Name[dI]).ToList();
//first_Name_List_1 now contains a list of everyone named X (Tom). We need to find people from group 2 who match Tom's birthday - regardless of name
//step 2 - find matching birthdays by JOINing the filtered name list against Group_2
DateTime[] merged_Dates = first_Name_List_1.Join(Group_2, d => d.birthday, b => b.birthday, (d, b) => b.birthday).ToArray();
//Step 3 - create filtered lists where Filtered_Group_1 contains ONLY people named Tom, and Filtered_Group_2 contains people with ANY name sharing Tom's birthday. No duplicates, no missing dates.
List<Person> Filtered_Group_1 = first_Name_List_1.Where(p => p.birthday.In(merged_Dates)).ToList();
List<Person> Filtered_Group_2 = Group_2.Where(p => p.birthday.In(merged_Dates)).ToList();
//Step 4 -- move on adn process the two filtered lists (outside scope of question)
//each name in Group_1 will then be compared to EVERY name in Group_2 sharing the same birthday
//compare_Groups(Filtered_Group_1,Filtered_Group_2)
});
}
}
public static class Extension
{
public static bool In<T>(this T source, params T[] list)
{
return list.Contains(source);
}
}
Here, the idea is to take two different master name lists from the DB and create sub-lists where dates match (one with only one name, and the other with all names) allowing for a one-to-many comparison based on datasets of the same length with matching date indices. Originally, the idea was to simply load the lists from the DB, but the lists are long and loading all name data and using SELECT/WHERE/JOIN is much faster. I say "much faster" but that's relative.
I've tried converting Group_1 and Group_2 to Dictionaries and matching dates by using keys. Not much improvement. Group_1 has about 12Million records (about 4800 distinct names with multiple dates each), and Group_2 has about the same, so the input here is 12Million records and the output is a bazillion records. Even though I'm running this method as a separate Task and queuing the results for another thread to process, it's taking forever to split these lists and keep up.
Also, I realize this code doesn't make much sense using class Person, but it's only a representative of the problem essentially using pseudocode. In reality, this method sorts multiple datasets on date and compares one to many for correlation.
Any help on how to accomplish filtering this one to many comparison in a more productive way would be greatly appreciated.
Thanks!
Code in the current format, I see way too many issues for it to become performance oriented with the kind of data you have mentioned. Parallelism is no magic pill for poor algorithm and data structure choice.
Currently for every comparison it goes for linear search O(N), thus making it M*O(N) for M operations, even if we make these operations O(logN), even better O(1), there would be a drastic improvement in the execution time.
Instead of taking Distinct and then searching in the Parallel loop using Where clause, use GroupBy to aggregate / group the records, and create a Dictionary in the same operation, which would ensure the easy search of records with a given name
var nameGroupList = Group_1.GroupBy(p => p.first_Name).ToDictionary(p => p.Key, p => p);
This will help you get rid of following two operations in the original code (one of them in Parallel is a repetitive operation, which hurts the performance big time)
List<string> distinct_Group_1_Name = Group_1.Select(p => p.first_Name).Distinct().ToList();
List<Person> first_Name_List_1 = Group_1.Where(m => m.first_Name == distinct_Group_1_Name[dI]).ToList();
The Dictionary will be of type Dictionary<string,IEnumerable<Person>>, thus you get the List of Person by name in O(1) time and there's no repetitive search. Another issue of the code that this would handle is recreation of list and as it searches through the original list / data.
Next part that needs to be handled, which is hurting the performance is the code like this
p.birthday.In(merged_Dates)
since in the extension methods you run the list.Contains, as an O(N) operation every time, which kills the performance. Following are the possible options:
Take the following operation too out of the Parallel loop:
DateTime[] merged_Dates = first_Name_List_1.Join(Group_2, d => d.birthday, b => b.birthday, (d, b) => b.birthday).ToArray();
Instead create another Dictionary of type Dictionary<string, Hashset<DateTime>>, by intersecting the data from Dictionary<string,IEnumerable<Person>> created earlier, using a Data from Group2, you can use the appropriate IEqualityComparer for DateTime and thus a ready reckoner for Date list / array would be available and needn't be created everytime:
personDictionary["PersonCode"].Intersect(Group2,IEqualityComparer(using Date))
For the final result please notice, you shall store the result as HashSet instead of List. The benefit would be the Contains would be O(log(N)) operation instead of O(N), thus making it much faster. In fact it is also fine to have the structure like Dictionary<string, Dictionary<DateTime,DateTime>>, which will make it O(1) operation.
Try these points and suggest if there's any improvement in the working of the code.

Compare List of Integers and add/remove the rows in database using the difference in result LinqtoSQL

Currently I'm working on a project using LinqtoSql and I would like to get an simpler solution for my current problem.
Example:
Lets say I got a table named Example with three rows (with values 1,2,4)
Now in code(c#) I got these values as a list of Integer(lets name it lstExisting)
Now in my method I got another List of Integer ( say lstCurrent) with Integers values (1,2,3)
Now I want to compare the both the list and find the difference of integers and update the database, so according to my example a new row with value 3 should be added and the existing row with value 4 should be deleted.
PS:(the integer values will be always unique and will be 1,2,3,4)
Linq solutions will be preferable but I don't mind other easier solutions.
Thanks
You need to find new items and to be deleted items using Except like:
var newItems = lstCurrent.Except(lstExisting).ToList();
var toBeDeletedItems = lstExisting.Except(lstCurrent).ToList();
Later you can iterate each list and Add/Delete accordingly.
Try using Contains(). With having two lists, you can write something like this. What this does is it iterates over each item in your methods list and checks the original to see if its there.
var lstExisting = getExistingList();
var lstCurrent = getCurrentList();
foreach(var currentInt in lstCurrent)
{
if(!lstExisting.Contains(currentInt))
{
//insert currentInt
}

How to group items by location and summing their quantity?

Everything shown below is purely pseudo-code to illustrate my problem.
My code has the following data structures:
// Used to define the ORDERS a customer has placed
List<Orders> myOrders;
Orders
{
String name
double quantity
String location // there are only 2 possible locations
}
// Used to define the ITEMS we have available
Item
{
String name
List<Parts> parts
}
// Used to define the PARTS to make an ITEM
Parts
{
String name
double quantity
}
The general breakdown seems nice and clear, but the customer requirements is where I am having issues and I am not sure how to best resolve the problem... (NOTE - I know using NAME and not an ID is bad to correlate - but this is a legacy system).
What I need to generate is the following:
- list of all PARTS by item needed from each LOCATION (only two possible) and the actual quantity defined by the orders
- display this on the screen to be printed
!! obviously you can have multiple order for the same parts at the same location and they should be added together to get a total.
So what I need to do:
- take each order and find the item
- take each item and find the parts
- for each part define its location (from order) and multiply by quantity (from order) and that is the # of parts for this location
The end result should be something like this (table grid form headers)
Item Part QuantityLocation1 QuantityLocation2
itm1 A 1 0
B 4 2
itm2 C 0 5
The issue being that obvious the PART class has no such concept as LOCATION...
Anyone have any suggestions?
The only solution I can see right now seems horrible ... create a new class (like PartswithLocation), add the location to it, and then recreate all the ITEM objects (that already exist) with this PartsWithLocation, then I can do some magic ORDER by ORDER to find the parts and based on Location and multiply in the quantity. The end result is a new List that I can then display in a grid or something (WPF)... But man this sounds ugly ...
This is purely a DESIGN related question, I am not looking for source code just some ideas on how to better resolve my issue.
Thank you for your time and help,
You shouldn't need any more than what you have now, if you manipulate your data on the fly correctly.
In pseudocode, here's what you need to do:
foreach(order in orders)
{
order.partlist = GetItem(order.name).parts; // partlist is a list of Part objects, not stored anywhere.
}
foreach(location in locations)
{
foreach(order in GetOrdersAtLocation(location))
{
foreach(part in order.parts)
{
location.partlist += { part.name, part.quantity * order.quantity }; // partlist is a list of parts and quantities, to be displayed.
}
}
}
I'd do this in LINQ, but since you asked for a design answer rather than code, this is more-or-less what it would unwrap to.

Categories

Resources