0

I have two lists. One filled with data from a database table, and the other is empty. I want to add the objects from orderList to VM.ViewOrders based on an object attribute called ProductName. If an object in VM.ViewOrder already contains the ProductName if it should not be added to the list. Instead, add one to the attribute numOfProd.

Any help would be appreciated!

I tried using foreach, but you cant modify the iterated list. I tried making a copy of the VM.ViewOrderList, but it gives me a: "System.OutOfMemoryException". Also tried to add index 0 of orderList to vm.ViewOrder,

List <Order> orderList = db.Orders.ToList();
vm.ViewOrders = new List<Order>();

foreach(Order order in orderList) {
  foreach(Order order1 in vm.ViewOrder)) {
    if (order.ProductName.Equals(order1.ProductName)) {
      order1.numOfProd++;
    } 
    else {
      vm.ViewOrder.Add(order);
      order.numOfProd = 1;
    }
  }
}

System.OutOfMemoryException

Collection was modified; enumeration operation may not execute.

Laczkó Örs
  • 1,082
  • 1
  • 18
  • 38
CSP
  • 53
  • 10
  • 1
    You should be using a `Dictionary` here not a list – Liam Sep 24 '19 at 11:01
  • Possible duplicate of [Collection was modified; enumeration operation may not execute](https://stackoverflow.com/questions/604831/collection-was-modified-enumeration-operation-may-not-execute) – ArunPratap Sep 24 '19 at 11:01
  • 1
    `db.Orders.ToList();` is also a bad idea. This is going to load your entire order table into memory (I'm guessing this is why you get an out of memory issue) when all you want is ones with a particular name. There is simply too much to fix here. Do some more reading and try again, your miles away here – Liam Sep 24 '19 at 11:04
  • 1
    This is called "Aggregation" and databases are *really* good at it. Look up `GroupBy` – Jamiec Sep 24 '19 at 11:15

2 Answers2

2

Use Linq GroupBy():

var groups = from order in orderList 
    group order by order.ProductName

You can use later

foreach (IGrouping<string,Order> g in groups){
    var order  = gr.First();
    order.numOfProd = gr.Count();
    vm.ViewOrder.Add(order);
}
Andrey Ischencko
  • 800
  • 1
  • 6
  • 18
0

I agree with Liam, you should use a dictionary for efficiency.

One other thing, in your statement you say to omit entries from the list if they are duplicates, but the code you provide suggests that you are actually trying to include them but with a count. So, maybe something like this:

        Dictionary<Order, int> dictionary = new Dictionary<IEntity, int>();
        foreach (Order order in ordersList) {
            if (dictionary.ContainsKey(order)) {
                dictionary[order] = dictionary[order] + 1;
            }
        }
        vm.ViewOrders = dictionary.Keys.ToList();
        foreach (Order order in vm.ViewOrders) {
            order.NumberOfProducts = dictionary[order];
        }

If you do want to omit the ones with duplicates then instead of the last part you could use something like this:

        vm.ViewOrders = dictionary.Keys.Where( obj => dictionary[obj] == 1).ToList();
        foreach (Order order in ordersList) {
            order.NumberOfProducts = dictionary[order];
        }

One other thing, the original orders list contains duplicates. It might be better to select these in a way that does not create duplicates in the first place. For example if products are in a different table you could use .Include in the query.

sjb-sjb
  • 1,112
  • 6
  • 14
  • Yes i want to go through each order to count the number of time one product has been sold. That way i can calculate the total value of each product that has been sold. – CSP Sep 24 '19 at 11:38