Trevor Trevor - 2 months ago 15
C# Question

C# update multiple element list item if item already exists otherwise add it as new item

Using C# I am trying to create a list with multiple elements adding new items if the main key is not already in the list, otherwise updating the item if it does. I've found quite a bit about each part, but am struggling with combining into a workable solution what I have found so far.

Below is commented code which compiles.

Problem:
Solution is adding all items as new items even if the key (ReceiptID) is already present in the list so there is something wrong with how I'm doing my checking.

Maybe problem:
Because I haven't been able to test the update if exist part, I don't know if I have that right.

Any guidance is appreciated.

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IList<Receipts> receiptList = new List<Receipts>()
// The real app reads through a temporary table in the database
// to pick up the line items of a sale. The objective is to combine those line
// items into a summary list called receipts based on the receipt number. For this
// runnable example, I add some data to the receipt list here.
{
new Receipts() { ReceiptID = 1, TotalPrice = 10, TotalCost = 5, Profit = 5, EmployeeID = 1 },
new Receipts() { ReceiptID = 2, TotalPrice = 15, TotalCost = 6, Profit = 9, EmployeeID = 1 },
new Receipts() { ReceiptID = 3, TotalPrice = 20, TotalCost = 7, Profit = 13, EmployeeID = 1 },
new Receipts() { ReceiptID = 4, TotalPrice = 25, TotalCost = 10.50M, Profit = 14.50M, EmployeeID = 1 },
};
// some dummy data to update list item with. Note: if receiptID is the same as one already in the list, employeeID
// will also be the same. (It's always the same employee that completes a whole transaction).
int[] receiptID = { 3, 4, 5, 5, 6, 7, 7, 8 };
decimal[] totalPrice = { 5, 6, 7, 8, 9, 10, 11, 12 };
decimal[] totolCost = { 2, 2.5M, 3, 3.5M, 4, 4.5M, 5, 5.5M };
decimal[] profit = { 3, 3.5M, 4, 4.5M, 5, 5.5M, 6, 6.5M };
int[] employeeID = { 1, 1, 1, 1, 2, 1, 1, 2 };

// This for loop represents the while loop reading the database table
for (int i = 0; i < 8; i++)
{
// first, check to see if the receiptID is already in the list. This is the
// part I am having trouble with.
Receipts r = new Receipts();
//EDIT:
//if (receiptList.Contains(new Receipts { ReceiptID = receiptID[i] })) <== original in question
//if (receiptList.Any(rc => rc.ReceiptID == receiptID[i])) <== Keyur PATEL's comment to question - Works
//if (receiptList.Any(o => o.ReceiptID == receiptID[i])) <== kurakura88 answer - Works
if (receiptList.Any(receipt => receipt.ReceiptID == receiptID[i])) // <== Eric Wu - Works

// END EDIT
{
// The code never enters here! <<=== This is what I need help with fixing
var tu = receiptList.Single(x => x.ReceiptID == receiptID[i]);
tu.TotalPrice += totalPrice[i];
tu.TotalCost += totolCost[i];
tu.Profit += profit[i];
// receiptID and employeeID are not updated as they don't change in this if loop.
}
else
{
// This should happen if the receiptID is not in the list, but it's happening
// every time.
r.ReceiptID = receiptID[i];
r.EmployeeID = employeeID[i];
r.TotalPrice = totalPrice[i];
r.TotalCost = totolCost[i];
r.Profit = profit[i];
receiptList.Add(r);
}
}
// Below here just displays the results in a sorted maner which is working ok.
var orderByValue = from s in receiptList
orderby s.ReceiptID
ascending
select s;

foreach (var item in orderByValue)
{
Console.WriteLine("Receipt: {0} Employee: {1} TotalPrice: {2} TotalCost: {3} Profit: {4}", item.ReceiptID.ToString(), item.EmployeeID.ToString(), item.TotalPrice.ToString(), item.TotalCost.ToString(), item.Profit.ToString());
}
Console.ReadLine();
}
}

public class Receipts
{
public int ReceiptID { get; set; }
public int EmployeeID { get; set; }
public decimal TotalPrice { get; set; }
public decimal TotalCost { get; set; }
public decimal Profit { get; set; }
}

}

/*
The output I am getting is (note each sale line is added to list):
Receipt: 1 Employee: 1 TotalPrice: 10 TotalCost: 5 Profit: 5
Receipt: 2 Employee: 1 TotalPrice: 15 TotalCost: 6 Profit: 9
Receipt: 3 Employee: 1 TotalPrice: 20 TotalCost: 7 Profit: 13
Receipt: 3 Employee: 1 TotalPrice: 5 TotalCost: 2 Profit: 3
Receipt: 4 Employee: 1 TotalPrice: 25 TotalCost: 10.50 Profit: 14.50
Receipt: 4 Employee: 1 TotalPrice: 6 TotalCost: 2.5 Profit: 3.5
Receipt: 5 Employee: 1 TotalPrice: 7 TotalCost: 3 Profit: 4
Receipt: 5 Employee: 1 TotalPrice: 8 TotalCost: 3.5 Profit: 4.5
Receipt: 6 Employee: 2 TotalPrice: 9 TotalCost: 4 Profit: 5
Receipt: 7 Employee: 1 TotalPrice: 10 TotalCost: 4.5 Profit: 5.5
Receipt: 7 Employee: 1 TotalPrice: 11 TotalCost: 5 Profit: 6
Receipt: 8 Employee: 2 TotalPrice: 12 TotalCost: 5.5 Profit: 6.5

What want to get is (same receiptID's should have values added together to one item in list):
Receipt: 1 Employee: 1 TotalPrice: 10 TotalCost: 5 Profit: 5
Receipt: 2 Employee: 1 TotalPrice: 15 TotalCost: 6 Profit: 9
Receipt: 3 Employee: 1 TotalPrice: 25 TotalCost: 9 Profit: 16
Receipt: 4 Employee: 1 TotalPrice: 31 TotalCost: 13 Profit: 18
Receipt: 5 Employee: 1 TotalPrice: 15 TotalCost: 6.5 Profit: 8.5
Receipt: 6 Employee: 2 TotalPrice: 9 TotalCost: 4 Profit: 5
Receipt: 7 Employee: 1 TotalPrice: 21 TotalCost: 9.5 Profit: 11.5
Receipt: 8 Employee: 2 TotalPrice: 12 TotalCost: 5.5 Profit: 6.5
*/


EDIT: After correcting the if statement, now get the following result which is correct:

Receipt: 1 Employee: 1 TotalPrice: 10 TotalCost: 5 Profit: 5
Receipt: 2 Employee: 1 TotalPrice: 15 TotalCost: 6 Profit: 9
Receipt: 3 Employee: 1 TotalPrice: 25 TotalCost: 9 Profit: 16
Receipt: 4 Employee: 1 TotalPrice: 31 TotalCost: 13.00 Profit: 18.00
Receipt: 5 Employee: 1 TotalPrice: 15 TotalCost: 6.5 Profit: 8.5
Receipt: 6 Employee: 2 TotalPrice: 9 TotalCost: 4 Profit: 5
Receipt: 7 Employee: 1 TotalPrice: 21 TotalCost: 9.5 Profit: 11.5
Receipt: 8 Employee: 2 TotalPrice: 12 TotalCost: 5.5 Profit: 6.5

Answer

The answer is that two objects of the same type may not always be equal, even if they have the same properties.

Thus, receiptList.Contains(new Receipts { ReceiptID = receiptID[i] })may never be true.

If you really want to check it in the list, and ReceiptID is the IDwith which you'll check, then do

receiptList.Any(receipt=>receipt.ReceiptID == receiptID[i])

Any will check for elements within the list and return trueif any are found with the conditional provided.