Comparing one list to another with a custom class - c#

So I wonder if I can compare two Lists without using foreach because the List is with a custom class. Inside the class, it contains two variables. one of them is called GUID. In order to access the GUID for List A, I use Any(x =>x.guid) And to access the same things in List B I have to do a foreach, which is like this, foreach(var x in List B){x.guid).
What I want to know is, is it possible to do it without the foreach? And if it is possible, how? I have been looking for an answer online but most of the example is looking at an item in one list. What I'm trying to do is compare one custom list to another, but only one variable inside the class instead of comparing the whole class.
The code below is how I do it, comparing one to another, but is there a more efficient way to do it
List<MySecondGameList> myloadinglist = JsonConvert.DeserializeObject<List<MySecondGameList>>(json);
foreach (var id in myloadinglist)
{
if (GameData_List.my_loading_list.Any(x => x.guid == id.guid))
{
Debug.Log("Matching!!!!!!!!!!!!!!!!");
continue;
}
GameData_List.my_loading_list.Add(id);
Debug.Log("It is loading");
}
}

You can hide loops within Linq queries, e.g.
List<MySecondGameList> myloadinglist = JsonConvert
.DeserializeObject<List<MySecondGameList>>(json);
// HashSet<T> provides faster Contains than List
HashSet<Guid> existing = GameData_List
.my_loading_list
.Select(item => item.guid)
.ToHashSet();
// We can put it compact with a help of AddRange instead of Add
GameData_List
.my_loading_list
.AddRange(myloadinglist.Where(item => !existing.Contains(item.guid)));

Related

Filter IEnumerable<object> based on whether string property contains any string value of another List<string>

I have an IEnumerable collection of custom objects types which contain a string property in JSON format. I also have a List collection which I need to use in order to filter the IEnumerable. I want to remove all items from the IEnumerable where the string property contains any string value from the List collection. I can pull this off by creating a temporary list and doing some looping but I'm looking for a more elegant solution and I haven't been successful yet.
foreach (var faction in excludedFactions)
{
cardsVM.Cards = repository.Cards.ToList()
.RemoveAll(c => c.Factions.Contains(faction));
}
This is one thing that I've tried. I've been playing around with LINQ statements for the last two hours and I can't make it work. If anyone could point me in the right direction (a proper function for my requirements) that would be more than appreciated.
Make a HashSet<string> of excluded factions, then use this set to filter your cards:
var excludedFactionSet = new HashSet<string>(excludedFactions);
cardsVM.Cards = repository.Cards
.Where(c => !excludedFactionSet.Contains(c.Factions))
.ToList();
Note that using excludedFactions directly is also possible. However, using HashSet<string> makes Contains(f) check work in constant, rather than linear, time.

Nested IEnumerable/Arrays, how to access a deeply nested property

So I have an IEnumerable object that contains a queue(list), each item contains 3 properties. One of those properties ("States") contains another array... and this array contains the property "Messages". Fairly confusing, that's why I took a photo of the local in debugging.
Had to edit sensible data out.
I only have this IEnumerable object to work with. How do I reach the property "message"?
I already tried some Lambda expressions... Like
var _message = _criticalData.Select(item1 => item1.States.Select(item2 =>item2.Messages).ToArray()).ToArray()
Then I can create a new array/list of strings and foreach each _message into it.
Atleast I think it works (can't test it at home). But it would also be really really slow. Is there any other way to do this?
You have to flatten out multiple lists, so use SelectMany like:
string[] output = _criticalData.SelectMany(outer =>
outer.States.SelectMany(inner => inner.Messages))
.ToArray();

Add, Update, Remove between collections using Linq

Here is my scenario. I am using WPF and making use of two way binding to show a collection objects received from a service call every 60 seconds. On the first call I create a collection of objects that will be displayed from the collection of service objects. On subsequent calls I need to compare the service collection to the existing collection and then do one of three things:
If the Item exists in both collections then update ALL of the values for the object in the Display collection with the values from the object in the service collection.
If the item Exists in the Service Collection and not the Display Collection then add it to the Display Collection.
If the Item exists in the Display collection and not the Service Collection then remove it from the Display collection.
I am looking for the best way to do this.
Adding & Removing
Is it smarter to do a Left Join here and return everything essentially unique to one side of the other and then add or remove that as appropriate?
Should I attempt to do a Union since Linq is supposed to merge the two and ignore the duplicates?
If so how does it decide uniqueness? Is it evaluating all the properties? Can I specify which collection to keep from and which to discard in merging?
Should I use Except to create a list of differences and somehow use that?
Should I create a new list to add and remove using Where / Not In logic?
Updating
Since the collections aren't dictionaries what is the best way to do the comparison:
list1.ForEach(x => list2[x.Id].SomeProperty = x.SomeProperty);
Is there some way of copying ALL the property values other than specifying each one of them similar to above? Can I perform some kind of shallow copy within Linq Without replacing the actual object that is there?
I don't want to just clear my list and re-add everything each time because the object is bound in the display and I have logic in the properties that is tracking deviations as values change.
You can use the except and intersect methods to accomplish most of what you are looking to do.
However, depending on the size of your objects this can be very resource intensive.
I would recommend the following.
var listIDsA = collectionA.Select(s => s.Id).Distinct().ToList();
var listIDsB = collecitonB.Select(s => s.Id).Distinct().ToList();
var idsToRemove = listIDsB.Select (s => !listIDsA.Contains(s.Id)).ToList();
var idsToUpdate = listIDsB.Select(s => listIDsA.Contains(s.Id)).ToList();
var idsToAdd = listIDsA.SelecT(s => !listIDsB.Contains(s.Id)).ToList();
Then using the three new collections you can add/remove/update the apporpriate records.
You can also use a hashedset instead of IEnumerables for better performance. This will require you to create an extension class to add that functionality. Here is a good explanation of how to do that (it's not complicated).
How to convert linq results to HashSet or HashedSet
If you do this, you will need to replace the .ToList() in the first two lines to .ToHasedSet()
For your comparison you need to overwrite equals and get hashcode
Object.GetHashCode Method
Then you can use List.Contains
List.Contains Method
If you can use HashSet then you will get better performance
Code not tested
ListDisplay.Remove(x => !ListSerice.Contains(x));
Foreash(ListItem li in ListDisplay)
{
ListItem lis = ListSerice.FirstOrDefault(x => x.Equals(li));
if (lis == null) continue;
// perform update
}
Foreach(ListItem li in ListSerice.Where(x => !ListDisplay.Contains(x))) ListDisplay.Add(li);

search arraylist by property using binarysearch

I currently have an arraylist containing classes in C#. The arraylist is filled like this:
foreach (XmlElement Path in locaties)
{
ISoundSource track = engine.AddSoundSourceFromFile(Path.InnerXml);
mixarray.Add(track);
}
then the array has many ISoundSource classes as its items. Now the thing that sets them apart in the array is their 'name' property. Later on I want to get the ISoundSource from the array by doing a search. I looked up on how to search arraylists and it is said to use a binarysearch but I don't see a way to look up an object with a certain property. How can I get the item from the array which has the name I specify?
You should probably use a Dictionary<,> as it will be much easier to maintain. Also, you should use List<> instead of ArrayList. If you must use BinarySearch, you will have to pass it a custom implementation of IComparer in order to have it use the Name property. Here's an example with a dictionary:
var dictionary = new Dictionary<string, ISoundSource>();
foreach (XmlElement Path in locaties)
{
ISoundSource track = engine.AddSoundSourceFromFile(Path.InnerXml);
mixarray.Add(track);
dictionary[track.Name] = track;
}
ISoundSource item = dictionary["MyTrackName"];
Check out the two parameter overload of BinarySearch which takes an IComparer as the second parameter - you then need to create a small class that inherits from IComparer that will compare the names of two of your Track objects, and pass an instance of this comparer into the BinarySearch.
There are many ways to do what you're asking for, and the right way depends on information that you haven't provided:
Does the Name property uniquely identify items?
Does every item have a Name?
Does the match have to be exact?
Is it important to know what order the items were originally added to the list in, i.e. the order that they appear in the source XML?
Are you trying to find items given their Name, or access them in order by their Name?
How important is it that this be efficient?
It may be that the right solution is to simply use LINQ to find an item:
ISoundSource track = mixarray
.Cast<ISoundSource>
.Where(x => x.Name == name)
.FirstOrDefault();
which will set track to the first item in the list whose name matches the value you're looking for, and to null if there's no match found. (If you use a List<ISoundSource> instead of an ArrayList, you can omit the Cast<ISoundSource> - one of many, many reasons to use List<T> over ArrayList in most cases.)
Most of the time I'll use a Dictionary<TKey, TValue> for this kind of thing, but that's because most the time the answers to those questions are yes, yes, yes, no, don't care about the order, pretty important.
For posterity, here is an alternative way to generate a dictionary using a simple Linq expression.
var dictionary = locaties
.Select(p->engine.AddSoundSourceFromFile(Path.InnerXml))
.ToDictionary(t->t.Name);
The .Select() transforms each node into an ISoundSource. When done, a collection (IEnumerable of ISoundSource) is returned. The .ToDictionary() then converts that list of ISoundSource to a Dictionary of string, ISoundSource.
This requires .NET Framework 3.5 or higher.

Sorting an List<> with my custom order which is stored in another List (C#)?

Can anyone help.. I have a generic list like so
IList<itemTp> itemTps;
itemTp basically is a class (has a number of properties) and one property on this is "code"
I need to be able to sort it an specific order which i have set in another List.
This list is a simple list that lists the order (starting from first to last) like say
code1
code3
code2
code5
(notice it goes from 1 to 3 to 2 to 5 - these are the names, they can be called anything .. the important thing is the order, it doesn't have anything to do with the numbers)
Basically i need ensure the items in itemTp sort according what is present in the other list...
So imagine my Ilist is like this code1,code2,code3,code5 - so once the sort is done
in my
IList<itemTp>
will contain 4 classes that are in order and have the property like code1,code3,code2,code5 (order change)
Any ideas how to do this?
You'll need to create an IComparer that will compare items based on the state of your other list. The advantage of using an IComparer is that you'll be able to build caching logic into your class to avoid repeated IndexOf() lookups if you need that optimization. Also, you'll be able to maintain multiple "other" lists that can be used when appropriate.
class ItemTpComparer : IComparer<itemTp>
{
private IList<codeTp> otherList;
public ItemTpComparer(IList<codeTp> otherList)
{
this.otherList = otherList;
}
public int Compare(itemTp a, itemTp b)
{
return otherList.IndexOf(a.Code) - otherList.IndexOf(b.Code);
}
}
And to perform the sort:
myList.Sort(new ItemTpComparer(otherList));
Sorry if something is wrong, I don't have a C# editor installed here, but it's something like this:
Array.Sort(
itemTps,
delegate(ItemTp a, ItemTp b)
{
return secondList.IndexOf(a.Code) - secondList.IndexOf(b.Code);
});
Note: it may not be the most efficient way, and also there is no checking whether the second list contains the Code at all.
[Edit] As Mehrdad said, Array.Sort doesn't work for generic IList<T>. To sort the array in place, you would need to use the ArrayList.Adapter method to create a wrapper, and then sort it. Since ArrayList.Sort doesn't support a delegate comparison, you need to put the anonymous delegate inside an implementation of IComparer<T>, .
If you need to sort it in a separate list, then it could have sense to use the logic above by creating an array first.
Using C# 3 and LINQ to Objects, I would just create a new list instead of trying to sort the existing one:
void Example()
{
IList<string> codes;
IList<ItemTp> itemTps;
itemTps = codes.Select(code => itemTps.Single(itp => itp.Code == code)).ToList();
}

Categories

Resources