Getting a single ID from loop using LINQ - c#

I have a method that groups items with the same productID together and then adds their quantity together for items in the shopping cart. Now I am trying to add to it where it also adds the quantity from a page that adds to the cart. So in a call to this method I send the productid and quantity selected by the user to add to the total quantity. But the problem I run into is comparing the productids of the items being sent to this method and the ones being grouped together.
Here is what I have below, the commented parts is what I attempted to do to fix this but found out my thoughts were wrong on it.
double totalQty = 0;
bool isQtyValid = true;
List<ShoppingCartDTO> shoppingList = ShoppingCart.Fetch(string.Format("WHERE SessionID='{0}'", sessionID));
foreach (ShoppingCartDTO temp in shoppingList)
{
var groupList = shoppingList.GroupBy(item => item.ProductID);
foreach (var m in groupList)
{
//var productID = m.Select(p => p.ProductID);
//int prodID = Convert.ToInt32(productID);
//if (ProductID == prodID)
//{
var sum = m.Sum(i => i.Quantity);
totalQty = sum;
//totalQty += qty;
isQtyValid = CheckQty(totalQty, temp.ProductID, temp.CustomerID);
//}
}
}
If anyone can provide some pointers or ideas I would really appreciate it. If it could use more explaining I can do that as well, or if you would like to see more code that I am using I can add that too.
Thank you to anyone that can help.

You grouped by the product ID, so it is the Key of your group:
foreach (var m in groupList)
{
var productID = m.Key;
// The rest of your code.
}

Related

Is it possible to Select a record for processing, if the selected record plus the two adjacent records add up to a certain quantity?

I would like to loop thru my records by Price (records are inserted by price, down to the penny) upon selecting a record, also grab the records one penny above and one penny below (if available), and total the Quantity. If the Quantity of these combined records is greater than a value, select it for further processing.
I think Grouping would work, but I'm not sure where to start. Could someone please point me in the right direction?
// Query the List
var CombinedQtyGT5K = (from a in TrackPrice
orderby a.Quantity descending
where a.Quantity > 5000
select new
{
a.Price,
a.Quantity,
});
foreach (var myprice in CombinedQtyGT5K )
{
//Process prices
}
public class PriceLevel
{
public decimal Price { get; set; }
public int Quantity { get; set; }
}
The first thing would be the grouping
var selected;
var group = CombinedQtyGT5K.GroupBy(x => x.Price);
//Not sure if you want to group it this way, cant comment so ill be guessing some
//things in this answer
Then add the loop
int c = group.count();
for(i=0; i < c, i++){
//add validation if up and down records avalible
//if c > 0 for example
if(group[c].Quantity + group[c-1].Quantity + group[c+1].Quantity > MinValue ){
selected = group[c];
//call the method to do your proccesing here
}
}

Loop to check for duplicate strings

I want to create a loop to check a list of titles for duplicates.
I currently have this:
var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
foreach (var x in productTitles)
{
var title = x.Text;
productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
foreach (var y in productTitles.Skip(productTitles.IndexOf(x) + 1))
{
if (title == y.Text)
{
Assert.Fail("Found duplicate product in the table");
}
}
}
But this is taken the item I skip out of the array for the next loop so item 2 never checks it's the same as item 1, it moves straight to item 3.
I was under the impression that skip just passed over the index you pass in rather than removing it from the list.
You can use GroupBy:
var anyDuplicates = SeleniumContext
.Driver
.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
.GroupBy(p => p.Text, p => p)
.Any(g => g.Count() > 1);
Assert.That(anyDuplicates, Is.False);
or Distinct:
var productTitles = SeleniumContext
.Driver
.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
.Select(p => p.Text)
.ToArray();
var distinctProductTitles = productTitles.Distinct().ToArray();
Assert.AreEqual(productTitles.Length, distinctProductTitles.Length);
Or, if it is enough to find a first duplicate without counting all of them it's better to use a HashSet<T>:
var titles = new HashSet<string>();
foreach (var title in SeleniumContext
.Driver
.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
.Select(p => p.Text))
{
if (!titles.Add(title))
{
Assert.Fail("Found duplicate product in the table");
}
}
All approaches are better in terms of computational complexity (O(n)) than what you propose (O(n2)).
You don't need a loop. Simply use the Where() function to find all same titles, and if there is more than one, then they're duplicates:
var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
foreach(var x in productTitles) {
if (productTitles.Where(y => x.Text == y.Text).Count() > 1) {
Assert.Fail("Found duplicate product in the table");
}
}
I would try a slightly different way since you only need to check for duplicates in a one-dimensional array.
You only have to check the previous element with the next element within the array/collection so using Linq to iterate through all of the items seems a bit unnecessary.
Here's a piece of code to better understand:
var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
for ( int i = 0; i < productionTitles.Length; i++ )
{
var currentObject = productionTitles[i];
for ( int j = i + 1; j < productionTitles.Length; j++ )
{
if ( currentObject.Title == productionTitles[j].Title )
{
// here's your duplicate
}
}
}
Since you've checked that item at index 0 is not the same as item placed at index 3 there's no need to check that again when you're at index 3. The items will remain the same.
The Skip(IEnumerable, n) method returns an IEnumerable that doesn't "contain" the n first element of the IEnumerable it's called on.
Also I don't know what sort of behaviour could arise from this, but I wouldn't assign a new IEnumerable to the variable over which the foreach is being executed.
Here's another possible solution with LINQ:
int i = 0;
foreach (var x in productTitles)
{
var possibleDuplicate = productTitles.Skip(i++).Find((y) => y.title == x.title);
//if possibleDuplicate is not default value of type
//do stuff here
}
This goes without saying, but the best solution for you will depend on what you are trying to do. Also, I think the Skip method call is more trouble than it's worth, as I'm pretty sure it will most certainly make the search less eficient.

Saving group of items from a list

What I am trying to accomplish is to group my list by supplier id. Iterate over that list calculate stuff etc and when finished pass that list on to save and clear the list out and start over again. The error is the : "collection was modified enumeration operation may not execute. c#". Now from googling it I'm just suming then pass that group off to save and clear the list rinse and repeat. I thought the clear would empty the list and allow for reuse.
Error - "collection was modified enumeration operation may not execute. c#"
group is grouping the list that is passed in to this method.
I created the count to see how many groups I had(6).
ProcessSpecialOrders receives a grouped list, po and the total.
What happens here is this will process through the first group once. Save to the DB then it will throw the error. If I refresh my page or check the DB I can see grop one has saved.
List<xxxxx> specialOrderList = new List<xxxxx>();
var group = from supplier in list group supplier by supplier.SupplierId;
var count = group.Count();
foreach (var grp in group.ToList())
{
specialOrderList.Clear();
foreach (var g in grp.ToList())
{
var orderItemList = new MOrderItem();
if (g.ItemType == "SD")
{
xxx.Id = 0;
xxx.ItemId = g.Id;
xxx.OnHandQty = g.OnHand;
xxx.ItemNo = g.ItemNumber;
xxx.ThisQty = 0;
xxx.NetCost = itemPrice.Where(x => x.ItemId == g.Id).Sum(l => (decimal?)l.NetCost * 1) ?? 0.0M;
xxx.ExtendedCost = 123.99M;
xxx.OnHandWhenBuildQty = 0;
xxx.OnOrderQty = matcoOrderItem.Where(x => x.ItemId == g.Id).Sum(x => (int?)x.ThisQty) ?? 0;
specialOrderList.Add(orderItemList);
}
}
ProcessSpecialOrders(specialOrderList, poNo, specialOrderTotal);
}

Assigning a 'day counter' for objects with DateTime?

I have a list of Order objects with a property OrderDate of type DateTime and I am trying to assign a 'DayCounter' for these objects that represents what order of the day it was, for example one day, I have 5 orders so every order gets a counter from 1 up to 5.
Here is what I tried:
orders.GroupBy(order => order.OrderDate.DayOfYear + order.OrderDate.Year)
.SelectMany(group =>
{
var count = 1;
group.Select(order =>
{
order.DayCounter = count;
count++;
return order;
});
return group;
});
the Order objects I get from this code all have DayCounter of 0
Any help would be appreciated
LINQ is not for modifying data, it is for selecting and projecting data. Your Select never gets run because Select is a lazy method and you never iterate over it. Use a normal foreach instead.
var groups = orders.GroupBy(order => order.OrderDate.Date);
foreach (var grouping in groups)
{
int orderCount = 1;
foreach (var order in grouping)
{
order.DayCounter = orderCount;
orderCount++;
}
}
I also changed your grouping key to be a more reliable seperator OrderDate.Date, your old method would consider days that are a year minus one day apart to be the same day.
Try using the overload of .Select which takes the index as a second parameter.
orders.GroupBy(order => order.OrderDate.DayOfYear + order.OrderDate.Year)
.SelectMany(group =>
{
group.Select((order,idx) =>
{
order.DayCounter = idx + 1;
return order;
});
return group;
});

Get maximum Count Lists Nested inside a list

I have a list of employees, and all of them have another list nested which is called the DisplayList.
Now not all the employees have the same amount of DisplayFields. So I wish to get those with the highest DisplayFields, so that I can incorporate everyone in the display.
At the moment I have the following :-
int iMaxDisplayCount = 0;
foreach (Employee employee in employeeList)
{
int iDisplayCount = employee.EmployeeDisplayCollection.Count;
if (iDisplayCount > iMaxDisplayCount)
iMaxDisplayCount = iDisplayCount;
}
var employees = employeeList.GroupBy(p => p.EmployeeDisplayCollection.Count == iMaxDisplayCount).Select(g => g.ToList());
foreach(var employeeHighList in employees)
{
foreach (var employee in employeeHighList)
{
}
}
however for some reason, I am getting all the employees in the employeeHighList and not just the ones who have the highest display count.
I think the GroupBy is not correct, but don't know what's wrong with it.
Any help will be very much appreciated!
Thanks
var max = employeeList.Max(e=>e.EmployeeDisplayCollection.Count);
var maxEmp = employeeList.Where(e=>e.EmployeeDisplayCollection.Count == max)
.ToList()

Categories