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.
Related
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.
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)
});
I'm basically looking for a 'google type' search of my database.
I'm currently creating a application which stores books (and authors), Games Movies (and more in the future). The application, obviously, also needs to be able to quickly search the database for any of these items.
Of course simply splitting up the games, books and movie searches is no problem, though I would really find it awesome if I had 1 search field for everything, mainly because I sometimes confuse books with movies xD
Now at first I thought this would be a nice way to go about it (simply only searching for books):
List<Book> books = (from b in le.Book
where (b.Title + " " + b.Author.FirstName + " " +
b.Author.Surname).Contains(search)
select b).OrderBy(b => b.Title).ToList();
This is easy, and works fine with a small database and when you type the search in the right order.
so using this a search would look like:
The fault in our stars John Green
but if someone were to type:
John Green The fault in our stars
The fault in our stars - John Green
or what ever variation you can come up with, it would fail.
I did find quite a nice example for a SQL query here: MYSQL search fields method but it's in SQL and I do not know how to re-write this to linq. Because the database (is going to) contain thousands of records, so I can't just do:
var total = (from b in le.Book
select new { b.ID, FullDescription = (b.Title + " " +
b.Author.FirstName + " " + b.Author.Surname) });
string[] searchArr = search.split(' ');
List<int> ids = new List<int>();
foreach(string s in searchArr)
{
ids.addRange((from t in total
where t.FullDescription.Contains(s).ToList());
}
The foreach loop would slow it down too much (I know there must be a better way to create a variable number of where statements but I don't know how to do that either).
But yeh the var total would become huge.
Then of course there is the part of making it a live search so it updates the list view every time a character is typed so if I type: "jo" I would get a list with results, then I can define it further by typing "joh" but would it be better to query the List of results I got from the last query or to re-query the whole database?
Also I need to take into account the Backspace, so if someone typed "jo" but wanted "ja" I need to re-query the entire database anyway right?
So what is the best practice for doing this? I've found quite some examples like the one mentioned but I'm searching for the fastest and "user proof" (meaning no matter how strange the search it still needs to come up with the right result)
My database model (only containing books, authors)
P.S. I am not the best database designer so if you find something you would do different let me know (still got a lot to learn)
You are asking an incredibly deep question, and I don't think there is a "right" answer but I do think there are "good" and "bad" approaches given your requirements and assumptions.
Fundamentally you are trying to accomplish the following:
Given a particular query string, you want to determine an ordering on your data row R
This ordering should be deterministic
This ordering should be easy to calculate
This ordering should reflect similarity or relevance between your search string and the members of R
You must first accept that unless we define the problem better, this is more of an art than a science. "Relevance" here is not well-defined. However, we can make some common-sense assumptions about what might be relevant. For instance, we might say that a relevant result has these qualities:
The search string is included in the members of R
More members of R with the search string indicates a more relevant result
Certain members of R are more important than others
We should allow for typos/mistakes - ie, a partial match is worth something
Then we can determine a "score" for R row as follows:
Each member of R gets a "weight" with a minimum value of 1 and no maximum value
The score for R is equal to the sum of the weight of each member divided by the "distance" between the member and the query string
The distance is defined in terms of a well-known string distance metric like Levenshetin or SoundEx
For instance, if your R has members Name, Description, and URL, you might weight these 100, 10, and 1, respectively, and apply the Levenshtein metric.
This is not even close to the tip of the iceberg, for it would be such a poor algorithm that it would be useless. Better approaches include cross-referencing members of your data row, looking up members against known dictionaries, and developing an evidence-based model for scoring results.
But scoring is a valid way to reduce the problem into an easier-to-state one.
let me try to explain this
I have two lists
1. list of employee objects
2.list of department objects(which has list of employee who can work in the department)
I want to be able to add a employee to a department in the list which has list of employees.
but I am getting null error
int empsize = allemployees.Count;
int Maxdepartment = 0;
foreach (employee employeeitem in allemployees)
{
Maxdepartment = employeeitem.alloweddepartments.Count;
for (int i = 0; i < Maxdepartment; i++)
{
int index = alldepartments.FindIndex(x => x.Name == employeeitem.alloweddepartments[i].ToString());
alldepartments[index].earlyshift.Add(employeeitem);
}
This looks like a very complex problem for me. Its for sure a optimization problem with many constraints, so i hope you are good at math ;-).
I would suggest to have a look at the Simplex Algorithm which will work very well for your problem if you have the mathematical know how to use it. There are some variations of the simplex too which maybe also work well.
There is an other way too where you just use the power of your computer to solve the problem. You could write a function which rates a solution and gives you some kind of benchmarkscore.
For instance you can rate the hour difference of provided and needed like every hour which is smaller then the needed is a -2, every hour which is bigger then needed is -1. So you can get a score to an employee assignment.
with this function you can start to randomly assign employees to departments (of course accourding to the min/max employees for each department) and then rate this solution using your function. so you can find a solution with a good score (if your defined function works well)
most of random assignments will be stupid of course but your computer generates million of solutions in seconds so chances are good that it will generate a good one after some time (dont think time is a big critera here cause once you have a solution it wont change very often)
How can you determine the current items position whilst looping through the collection?
I'm working through decision data, grouped by each client, but I have some business logic which depends on the "position" in the set, i.e. 1st, 2nd, 3rd, etc. in conjunction with other properties of the record, e.g. if it's the 3rd decision about a client and their rating in the instance is A then ...
var multiples = from d in context.Decision_Data
group d by d.Client_No
into c
where c.Count() > 1
select c;
foreach (var grouping in multiples)
{
foreach (var item in grouping)
{
// business logic here for processing each decision for a Client_No
// BUT depends on item position ... 1st, 2nd, etc.
}
UPDATE: I appreciate I could put a counter in and manually increment, but it feels wrong and I'd of thought there was something in .NET to handle this ??
Something like this:
foreach (var grouping in multiples)
{
foreach (var x in grouping.Select(index,item) => new {index, item})
{
// x.index is the position of the item in this group
// x.item is the item itself
}
}
Side note: you can make the implementation of your LINQ query a bit more efficient. Count() > 1 will completely enumerate each group fully, which you are likely to do in the foreach anyway. Instead you can use Skip(1).Any(), which will stop iterating the group as soon as it finds two items. Obviously this will only make a real difference for (very) large input lists.
var multiples = from d in context.Decision_Data
group d by d.Client_No
into c
where c.Skip(1).Any()
select c;
There isn't anything offered by the standard foreach. Simply maintain an external count.
There is an overload on the Enumerable.Select extension method that provides the index of the current item:
http://msdn.microsoft.com/en-us/library/bb534869
But without knowing what your code is trying to do in the foreach I cannot really offer an example of using it. In theory you could project an anonymous type that has the index stored and use that later on with the foreach. It appears that jeroenh's answer went down this route.
As Adam stated, you could either go with Adams solution, or do a ToList() on the query to be able to do
multiples.IndexOf(grouping)
I fail to see how you can have any certainty about your decisions order.
I'm guessing your data come from a long-term data source (e.g. data base or such) and that you doesn't have any control on the order in which the decision are fetched from the data source, especially after applying a "group by".
I would add an "order" field (or column) to the Decision entity to track the order in which the decision were made that would be set while adding the Decision to the data source.
That way, you could directly use this field in your business logic.
There must be many ways to achieve the tracking of decision order but without, you can't even be sure in what order they have been made.