- Home /
The question is answered, right answer was accepted
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.
Answer by Bunny83 · Mar 11, 2014 at 01:06 PM
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.
Thanks, I think I will try the Boolean idea, it seems by far the easiest. I will close this now, Thanks!
Answer by stevethorne · Mar 11, 2014 at 12:56 PM
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.
It could be more succinctly put as:
var results = list.Except(list2).Concat(list2.Except(list)).ToList();
I think he only wants the results from one list (the Level list). I don't think it's possible to have a level in the bought list but not in the Levels list. The title is a bit misleading i guess. I also thought he wants a "xor combine" but looking at the problem it seems he just wants the levels which aren't bought yet.
So then he wants this?
results = BoughtLevels.Except( Levels ).ToList();
No, the other way round. Levels contains all levels. Omitting all levels from a subset would give you an empty list ;)
BuyableLevels = Levels.Except( BoughtLevels ).ToList();
However as i said in my answer it seems he creates multiple instances for the same level so all those linq functions won't work since you can't compare the instances directly.
Ah I see. Well then you should really view Bunny's answer here.