1

I've come across this a few times and I feel like I'm always writing unessasary code. I'm looking for the most efficient way to compare the value of a field in a list with another list.

For example, let's say I have a viewmodel such as the following:

    public class UserContractAddDTO
    {
        public string ContractId { get; set; }
        public bool ContractSelected { get; set; }
        public string UserName { get; set; }
    }

I put the values of the table into this view model, and then pass it back as an IEnumerable:

 IEnumerable<UserContractAddDTO> jsonArray

Now that I have this IEnumerable of UserContractAddDTO's, I will need to loop through it to compare the values that exist already in the database.

Let's say ContractSelected has been updated for some of the rows, so it's set to true. Normally I would use a foreach loop, but it would need to know if the row already exists to avoid duplicate rows.

        foreach (UserContractAddDTO u in jsonArray)
        {
            var a = u.ContractId.ToString();
            if (u.ContractSelected == true)
            {
                foreach (UserPlanSponsorContract up in UserContractList)
                {
                    if (up.PlanSponsorContractId.ToString() == a  && up.UserId == userId)
                    {
                        //update row
                        var row = _context.UserPlanSponsorContracts.Where(r => r.PlanSponsorContractId.ToString() == u.ContractId && r.UserId == userId).FirstOrDefault();
                        row.IsVerified = true;
                        _context.SaveChanges();
                    }
                    else
                    {
                        //add row
                        _context.UserPlanSponsorContracts.Add(new UserPlanSponsorContract
                        {
                            UserId = userId,
                            PlanSponsorContractId = Convert.ToInt32(u.ContractId),
                            IsVerified = true,
                            ContractAdminEmailSent = false,
                            AppliedDate = DateTime.Now,
                            ReverifyReminder = 0
                        });
                        _context.SaveChanges();
                    }
                }
            }
            else
            {
            foreach (UserPlanSponsorContract up in UserContractList)
            {
                if (up.PlanSponsorContractId.ToString() == a && up.UserId == userId)
                {
                    //update row
                    var row = _context.UserPlanSponsorContracts.Where(r => r.PlanSponsorContractId.ToString() == u.ContractId && r.UserId == userId).FirstOrDefault();
                    row.IsVerified = false;
                    _context.SaveChanges();
                }
                }
            }
        }

This is one approach I've tried, but I'm looking for a more efficient way of doing it. Thoughts?

stelioslogothetis
  • 9,371
  • 3
  • 28
  • 53
Dylan
  • 37
  • 2
  • 7
  • Why are you converting `u.ContractId` to a string and then comparing it to other things you also have to convert to a string? If you have to convert `u.ContractId` to a string, why do you have a `Convert.ToInt32()` when assigning to `PlanSponsorContractId`? – NetMage Jul 25 '17 at 22:47
  • Where did `UserContractList` come from? – NetMage Jul 25 '17 at 22:49

3 Answers3

2

As a general rule, the approach you need to take heavily depends on the structure of your program. It basically comes down to reference type equality vs value equality.

If the objects in both lists are, in fact, the same object, then you do the following:

var diff = myList.Except(myOtherList).ToList();

If, like in your case, you are dealing with value equality (two different objects, but the same "value"), then you first need to tell C# what makes two instances of your object equal. You need to define equality.

For doing this there are two schools of thought. You can modify your existing class and make it implement IEquatable, or you can create a brand new class which implements IEqualityComparer, whos job it will be to compare instances of your class.

You can see a detailed example of the former approach in this answer by Prashanth Thurairatnam, and an one of the latter approach in this answer by Jon Skeet.

In both cases, you will have to implement two methods, Equals(T, T) and GetHashCode(T). These methods will be used to figure out what makes two instances of your object equal. Once you have done that the only code you need is that which I have written above (the only difference being that you also have to provide your comparer as an argument if you go with the IEqualityComparer approach).

stelioslogothetis
  • 9,371
  • 3
  • 28
  • 53
0

LINQ isn't really designed for updating, so you would normally still need a foreach loop to process the results from the LINQ, but you don't need to repeat yourself so much. Without knowing where UserContractList came from, I can't say if this could be further simplified.

foreach (UserContractAddDTO u in jsonArray) {
    var intContractId = Convert.ToInt32(u.ContractId);
    foreach (UserPlanSponsorContract up in UserContractList) {
        if (up.PlanSponsorContractId == intContractId && up.UserId == userId) {
            //update row
            var row = _context.UserPlanSponsorContracts.Where(r => r.PlanSponsorContractId == intContractId && r.UserId == userId).FirstOrDefault();
            row.IsVerified = u.ContractSelected;
        }
        else {
            //add row
            _context.UserPlanSponsorContracts.Add(new UserPlanSponsorContract {
                UserId = userId,
                PlanSponsorContractId = intContractId,
                IsVerified = true,
                ContractAdminEmailSent = false,
                AppliedDate = DateTime.Now,
                ReverifyReminder = 0
            });
        }
    }
    _context.SaveChanges();
}
NetMage
  • 26,163
  • 3
  • 34
  • 55
-1
using System;
using System.Collections.Generic;

namespace SwapArrayVal
{
    class A
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {

            List<A> list = new List<A>();
            list.Add(new A() { ID = 1, Name = "Pradeep" });
            list.Add(new A() { ID = 2, Name = "Binod" });


            IEnumerable<A> en1 = list;


            List<A> list2 = new List<A>();
            list2.Add(new A() { ID = 1, Name = "Pradeep" });
            list2.Add(new A() { ID = 2, Name = "Binod" });


            var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
            string outputOfInts = serializer.Serialize(list);
            string outputOfFoos = serializer.Serialize(list2);



            IEnumerable<A> en2 = list2;
            if (outputOfInts == outputOfFoos)
            {
                Console.Write("True");
            }

            Console.ReadLine();
        }
    }
}

Here you can compare two IEnumerable list

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
  • well... you certainly compare 2 small lists. But this is highly inefficient, uses high amounts of memory, and has the "string size" limitation problem. Imagine this scenario: First item of the list is different, then there is no need to continue the comparison (you achieved the goal fast), but using your solution you still need to go through every item in the list, so every case is worst case scenario. Also imagine having a list of thousands of objects, each with multiple properties (including lists)... Try to add information on why this is faster than OP's or other solutions. – c-chavez Feb 03 '22 at 13:55