how to check if object already exists in a list

C#LinqList

C# Problem Overview


I have a list

  List<MyObject> myList

and I am adding items to a list and I want to check if that object is already in the list.

so before I do this:

 myList.Add(nextObject);

I want to see if nextObject is already in the list.

The object "MyObject" has a number of properties but comparison is based on matching on two properties.

What is the best way to do a check before I add a new "MyObject" to this list of "MyObject"s.

The only solution I thought up was to change from a list to a dictionary and then make the key a concatenated string of the properties (this seems a little unelegant).

Any other cleaner solutions using list or LINQ or something else?

C# Solutions


Solution 1 - C#

It depends on the needs of the specific situation. For example, the dictionary approach would be quite good assuming:

  1. The list is relatively stable (not a lot of inserts/deletions, which dictionaries are not optimized for)
  2. The list is quite large (otherwise the overhead of the dictionary is pointless).

If the above are not true for your situation, just use the method Any():

Item wonderIfItsPresent = ...
bool containsItem = myList.Any(item => item.UniqueProperty == wonderIfItsPresent.UniqueProperty);

This will enumerate through the list until it finds a match, or until it reaches the end.

Solution 2 - C#

Simply use Contains method:

bool alreadyExist = list.Contains(item);

Note that it works based on the equality function Equals. Check the example of the link above if you need to implement Equals function.

Solution 3 - C#

If it's maintainable to use those 2 properties, you could:

bool alreadyExists = myList.Any(x=> x.Foo=="ooo" && x.Bar == "bat");

Solution 4 - C#

Are you sure you need a list in this case? If you are populating the list with many items, performance will suffer with myList.Contains or myList.Any; the run-time will be quadratic. You might want to consider using a better data structure. For example,

 public class MyClass
    {
        public string Property1 { get; set; }
        public string Property2 { get; set; }

    }

    public class MyClassComparer : EqualityComparer<MyClass>
    {
        public override bool Equals(MyClass x, MyClass y)
        {
            if(x == null || y == null)
               return x == y;

            return x.Property1 == y.Property1 && x.Property2 == y.Property2;
        }

        public override int GetHashCode(MyClass obj)
        {
            return obj == null ? 0 : (obj.Property1.GetHashCode() ^ obj.Property2.GetHashCode());
        }
    }

You could use a HashSet in the following manner:

  var set = new HashSet<MyClass>(new MyClassComparer());
  foreach(var myClass in ...)
     set.Add(myClass);

Of course, if this definition of equality for MyClass is 'universal', you needn't write an IEqualityComparer implementation; you could just override GetHashCode and Equals in the class itself.

Solution 5 - C#

Another point to mention is that you should ensure that your equality function is as you expect. You should override the equals method to set up what properties of your object have to match for two instances to be considered equal.

Then you can just do mylist.contains(item)

Solution 6 - C#

Edit: I had first said:


What's inelegant about the dictionary solution? It seems perfectly elegant to me, especially since you only need to set the comparator in creation of the dictionary.


Of course though, it is inelegant to use something as a key when it's also the value.

Therefore I would use a HashSet. If later operations required indexing, I'd create a list from it when the Adding was done, otherwise, just use the hashset.

Solution 7 - C#

Here is a quick console app to depict the concept of how to solve your issue.

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

namespace ConsoleApplication3
{
	public class myobj
	{
		private string a = string.Empty;
		private string b = string.Empty;

		public myobj(string a, string b)
		{
			this.a = a;
			this.b = b;
		}

		public string A
		{
			get
			{
				return a;
			}
		}

		public string B
		{
			get
			{
				return b;
			}
		}
	}


	class Program
	{
		static void Main(string[] args)
		{
			List<myobj> list = new List<myobj>();
			myobj[] objects = { new myobj("a", "b"), new myobj("c", "d"), new myobj("a", "b") };


			for (int i = 0; i < objects.Length; i++)
			{
				if (!list.Exists((delegate(myobj x) { return (string.Equals(x.A, objects[i].A) && string.Equals(x.B, objects[i].B)) ? true : false; })))
				{
					list.Add(objects[i]);
				}
			}
		}
	}
}

Enjoy!

Solution 8 - C#

Simple but it works

MyList.Remove(nextObject)
MyList.Add(nextObject)

or

 if (!MyList.Contains(nextObject))
    MyList.Add(nextObject);

Solution 9 - C#

A collection can be used as a dictionary, where the difference is that you don't need to a reference to Microsoft Scripting Runtime or use late binding. Please note that in this case the key must be a string. In my case the key(number) is integer, but declared as string. You can create a custom Boolean function to check if the key exists in the list. There is a good article be Paul Kelly on ExcelMacroMastery.com

' Function to check if item in the collection already exists
Function Exists(coll As Collection, key As String) As Boolean

    On Error GoTo EH

    IsObject (coll.Item(key))
    Exists = True

EH:
End Function

end you can use it like this

For i = 3 To lastRow
    
        ' Ignore the Normal areas
        If rg.Cells(i, 1).value <> "Normal" Then
        
            number = rg.Cells(i, 1).value
            
            ' Check if the area exist in the collection using a custom function Exists
            If Exists(coll, number) = False Then
            
                Set oRiskArea = New clsHighRiskArea
                oRiskArea.number = number
                coll.add key:=oRiskArea.number, Item:=oRiskArea
                
            Else
                Set oRiskArea = coll(number)
            End If
            
            With oRiskArea
        
                .name = rg.Cells(i, 2).value
        
            End With
            
        End If

    Next i

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionleoraView Question on Stackoverflow
Solution 1 - C#Rex MView Answer on Stackoverflow
Solution 2 - C#AhmadView Answer on Stackoverflow
Solution 3 - C#p.campbellView Answer on Stackoverflow
Solution 4 - C#AniView Answer on Stackoverflow
Solution 5 - C#Fiona - myaccessible.websiteView Answer on Stackoverflow
Solution 6 - C#Jon HannaView Answer on Stackoverflow
Solution 7 - C#DougView Answer on Stackoverflow
Solution 8 - C#Opt PrutalView Answer on Stackoverflow
Solution 9 - C#nikolayDudrenovView Answer on Stackoverflow