Compare two Lists and get the items that are not in both lists

Having two lists (LIST<>), how can I compare two Lists get the items that not in both lists , I know I probably need to use LINQ and I have been trying to learn about it but I’m not good enough with it yet.

I have two lists, one is for levels that are available for sale and other is the levels already bought by the player, I want to display at the shop only the levels that not bought by the player yet.

This is the part where I display the levels that are for sale:

Scrollbar = GUILayout.BeginScrollView(Scrollbar);
		
		for (int n = 0; n < Levels.Count; n++){
		
			GUILayout.Box (Levels[n].newName);
			GUILayout.Box (Levels[n].newDescription);
			GUILayout.Box ("" + Levels[n].newPrice);
			
			if (GUILayout.Button("Buy")){
					if (Status.Gold < Levels[n].newPrice){
						Status.BoughtLevels.Add(new Level (Levels[n].newName,Levels[n].newDescription,Levels[n].newIndex,Levels[n].newPrice));
						Status.Gold -= Levels[n].newPrice;
						}
						
					
					}
				}

I have been trying to use if (Status.BoughtLevels.Contains(i=>i.Name != Levels[n].newName)){ Where Status.BoughtLevels are the levels bought by the player and while I don’t get any error it simply doesn’t display anything.

Using LINQ here’s what you can do. There may be better alternatives, but this is the best I can think of.

List<string> list;
List<string> list2;
List<string> results = list.Except( list2 ).ToList().AddRange( list2.Except( list ) );

EDIT: I should also mention that this is the best I can think of in LINQ. There are probably better solutions outside of LINQ.

So you basically want to subtract the “bought level set” from the “available level set” as said in set theory.

either iterate through your available list and doing a Contains check each iteration or create a copy of the available list, iterate through the bought list and remove each item from the copied list.

BTW: it seems you create multiple Level objects for each level. This will always get you in trouble. Make sure you only create 1 instance for each level.

So when you bought a level you should do

Status.BoughtLevels.Add(Levels[n])

If you have only one instance per level you can simply do:

// first way
for (int n = 0; n < Levels.Count; n++)
{
    if (!Status.BoughtLevels.Contains(Levels[n]))
    {
        // display the level so it can be bought
    }
}

Keep in mind that this is a O(n²) complexity. So if you have about 100 levels it has to do 10000 comparisons internally. If you do this in OnGUI you have to do this twice each frame which could get you in trouble. It’s better to filter out which levels can be bought beforehand and just display this list

// second way
List<Level> GetBuyableLevels()
{
    var tmp = new List<Level>(Levels); // get a copy of all levels
    foreach(var L in Status.BoughtLevels)
        tmp.Remove(L);
    return tmp;
}

This has still a O(n²) compplexity (you can’t really get around this when using two lists) but you only do it once when the BoughtLevels list changes

Another common and more easier way is to add a “bought” boolean to your level object. That way you don’t need a seperate list and each level has it’s bought state. This simplyfies all lookups to O(n). You can simply filter the list with linq like this:

foreach(Level L in Levels.Where(i=>!i.bought))
{
    // draw GUI
}

This would display only those levels which aren’t bought yet. To “buy” one you just set the levels bought field to true.

Without more information on how you save / get the bought levels / available levels i can’t suggest anything more specific.