1

I apologize for posting such a seemingly simple question. I know there are many similar questions already posted (and I have looked at many of these before posting my question), but I struggle to apply the answers to my situation. I am relatively new to C#, and would appreciate your input and help.

How can I compare my 2 Lists with a foreach loop, and create a new List with the records found that does not exist in my comparison?

Below is the code outline I already have, and comments of what needs to happen:

private void updateHolidays()
{
    List<Holiday> localHolidays = getLocalHolidays();
    List<Holiday> remoteHolidays = getRemoteHolidays();

    List<Holiday> holidayDifference = new List<Holiday>();

    foreach (Holiday holiday in remoteHolidays)
    {
        if (true) // holiday does not exist in localHolidays
        {
            // add holiday to holidayDifference
            holidayDifference.Add(holiday);
        }
    }

    createNewHolidays(holidayDifference);
}

Thank you in advance!

onmyway
  • 1,435
  • 3
  • 29
  • 53

6 Answers6

3

You can use Linq's Except extension method:

holidayDifference = remoteHolidays
    .Except(localHolidays)
    .ToList();

Note that this will also requires Holiday to implement a valid Equals method of IEquatable<Holiday> method override, also GetHashCode must return an identical hash for two Holidays for which Equals returns true.

Also, Except is an extension which returns (in this case) an IEnumerable<Holiday> therefore you will have to use the ToList extension method in order to retrieve a List<Holiday>

Alternatively, you can use the other overload of Except which allows you to provide an IEqualityComparer<Holiday> instead of modifying your original class.


Example with strings:

List<string> holidayDifference = new List<string>();
List<string> remoteHolidays = new List<string> { "1", "2", "3" };
List<string> localHolidays = new List<string> { "1", "3" };
holidayDifference = remoteHolidays
    .Except(localHolidays)
    .ToList();
holidayDifference.ForEach(Console.WriteLine);

Output:

2


Example With Holiday : IEquatable<Holiday>:

class Holiday : IEquatable<Holiday>
{
    public string Name { get; set; }

    public bool Equals(Holiday other)
    {
        return Name == other.Name;
    }

    // GetHashCode must return true whenever Equals returns true.
    public override int GetHashCode()
    {
        //Get hash code for the Name field if it is not null.
        return Name?.GetHashCode() ?? 0;
    }
}

public class Program
{
    public static void Main()
    {
        List<Holiday> holidayDifference = new List<Holiday>();
        List<Holiday> remoteHolidays = new List<Holiday>
        {
            new Holiday { Name = "Xmas" },
            new Holiday { Name = "Hanukkah" },
            new Holiday { Name = "Ramadan" }
        };
        List<Holiday> localHolidays = new List<Holiday>
        {
            new Holiday { Name = "Xmas" },
            new Holiday { Name = "Ramadan" }
        };
        holidayDifference = remoteHolidays
            .Except(localHolidays)
            .ToList();
        holidayDifference.ForEach(x => Console.WriteLine(x.Name));
    }
}

Output:

Hanukkah

Sebastian Brosch
  • 42,106
  • 15
  • 72
  • 87
Tamir Vered
  • 10,187
  • 5
  • 45
  • 57
2

Assuming you have a overload of the Equals method and Holiday objects are compareable

List<Holiday> holidayDifference = remoteHolidays.Except(localHolidays).ToList();
fubo
  • 44,811
  • 17
  • 103
  • 137
2

The easiest way would be using LinQ. The Except methods returns all items from the source which not exists in the second list.

holidayDifference = remoteHolidays.Except(localHolidays).ToList();

The Except method accepts a optional second parameter to customize the comparison. If you don't pass a IEqualityComparer<T> the standard comparison with the Holiday.Equals method will be used. Alternatively you can override this method instead of passing a comparer.

Like most of LinQ methods, Except returns a IEnumerable<T>, this can be easily converted to a List<T> with the ToList method.

The MSDN documentations are linked inline.


If you still want to implement this yourself, you can use the Contains method of List<T>:

foreach (Holiday holiday in remoteHolidays)
{
    if (!localHolidays.Contains(holidy))
    {

An alternative to Contains would be LinQs Any which allows you to compare your objects with a function/lamda expression.

Graham
  • 7,431
  • 18
  • 59
  • 84
Koopakiller
  • 2,838
  • 3
  • 32
  • 47
1
private void updateHolidays()
{
    List<Holiday> localHolidays = getLocalHolidays();
    List<Holiday> remoteHolidays = getRemoteHolidays();

    List<Holiday> holidayDifference = new List<Holiday>();

    foreach (Holiday holiday in remoteHolidays)``
    {
        if (localHolidays.Contains(holiday)) 
        {
            // add holiday to holidayDifference
            holidayDifference.Add(holiday);
        }
    }

    createNewHolidays(holidayDifference);
}
DaveHutchy
  • 57
  • 6
0

If you insist on foreach loop you can use HashSet<Holiday> to store the Holidays to exclude:

HashSet<Holiday> hs = new HashSet<Holiday>(localHoliday);

foreach (Holiday holiday in remoteHolidays)
  if (!hs.Contains(holiday))
    holidayDifference.Add(holiday);
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
0

It depends if both the lists contain the same Holiday objects, if they are the same objects that are in both lists then you don't need to make a comparer that implements IEqualityComparer. I will assume they are not the same objects and hence you are doing comparisons by value rather than reference.

The quickest way of doing this is without a foreach loop, you can do this using LINQ as such.

var holidayDifference = remoteHolidays.Except(localHolidays, new HolidaysComparer()).ToList();

If you want to do it using a foreach loop you can do it several ways. You can do it using a HashSet (make sure to initialize the set with a HolidayComparer) by iterating on the values in localHolidays and adding them to the set, then pulling out the ones from remoteHolidays that aren't in that set. However the easy way would be to use the LINQ contains function (The except function above essentially wraps this into a loop).

var holidayDifference = new List<Holiday>();
var comparer = new HolidayComparer();
foreach(Holiday holiday in remoteHolidays)
{
    if(!localHolidays.Contains(holiday, comparer)) 
        holidayDifference.Add(holiday);
}

If you are comparing each holiday by reference then you won't need to implement the IEqualityComparer interface.

To learn how to implement this interface look here IEqualityComparer Interface

Kadir
  • 1