I want a method to update certain entries of an IEnumerable. I found that doing a foreach over the entries and updating the values failed as in the background I was cloning the collection. This was because my IEnumerable was backed by some LINQ->SQL queries.
By changing the method to take a List I have changed this behavior, Lists are always mutable and hence the method changes the actual objects in the list. Rather than demand a List is passed is there a Mutable interface I can use?
// Will not behave as consistently for all IEnumerables
public void UpdateYesterday (IEnumerable<Job> jobs) {
foreach (var job in jobs.Where(x => x.date == Yesterday)) {
job.done = true;
}
}
...
public class Job {
...
public DateTime Date { get; set; }
}
You're not changing the list at all here - the collection itself could be immutable and this code would still "work". You're changing the data within the items in the collection.
Imagine a row of houses - that's pretty immutable (creating or destroying a house is tricky) but you can still change the contents of those houses.
So, what you really need to know is whether the elements of the collection are going to be cloned or not... whether you'll get a collection of "new" jobs each time you perform the query, or whether it'll use the existing objects.
Here's one hypothetical way this might happen. Say you have a method that takes an existing data source and creates Job objects from that source.
Something like this:
public IEnumerable<Job> GetJobs() {
var rows = GetRowsFromDatabaseTable();
foreach (var row in rows)
yield return new Job(row.Name, row.Date, row.Etc);
}
Then if you had code that did this:
var jobs = GetJobs();
UpdateYesterday(jobs);
foreach (var job in jobs)
Console.WriteLine(job);
You would find that the jobs you printed using Console.WriteLine did not reflect the updates you performed in UpdateYesterday. This is because UpdateYesterday would have enumerated over the new objects created by GetJobs, and then your foreach loop would have enumerated over a new set of new objects created (again) by GetJobs.
On the other hand, if you simply changed that first line to this:
var jobs = GetJobs().ToList();
Then you would have put all those new objects created by GetJobs into a list, where they would persist, and thus your UpdateYesterday and foreach loop would be referring to the same objects (the ones in the list you created).
Does that make sense? I think this could very well be the problem you're encountering (in which case, the answer is indeed to construct a collection such as a List<Job> in memory from which you can access members to manipulate).
In answer to your question about being able to tell if an IEnumerable is mutable or not, though... well, I really don't think that is possible.
I've removed the old answer, as the new one seems to be what the OP needed.
Related
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)));
I am building a control in xamarin forms that binds to a list of objects. To get this binding to work I need to use observable collections (otherwise propertychanged methods don't fire).
I noticed a really frustrating interaction as a result of needing to use OC's as opposed to lists. Whenever the binded OC updates, the values in my controls are automatically updated, even if they are just references of the OC, Here is how i am copying the OC.
//Internal list of events
private List<EventItem> _events;
void OnEventsChanged(ObservableCollection<EventItem> eventsCollection)
{
//Error handle
List<EventItem> events = eventsCollection.ToList();
//Do something
_events = events;
}
The problem comes when the OC updates, I want to check for new/deleted AND altered objects. The issue is that when the OC updates, it is updating the internal list (_events) aswell. This means when I go to do comparisons between the old & new values, they are the same.
Honestly I don't really understand how c# handles copying references of objects around, I had a similar issue a while back with DateTime.Now being calculated as opposed to copying the value of the already initialised object.
var time = DateTime.Now;
await Task.Delay(1000);
var time2 = time; //This is 1 second later than time, not the value of time (which is what I wanted)
I have used Objective-C in the past and that has the concept of MutableCopy where you can assign a new list from an existing one, they have the same values but aren't linked.
How can I do this in C# so that my controls internal list is only updated by me and not the OC?
Thanks
That's perfectly normal. If I have enough time, I'll try to explain it to you.
In a nutshell, the observableList (or a List) is a list of reference to the objects and not a list of objects. The thing is that the objects are not copied inside a list but the list contains a reference to the different objects. That means that if you do something like ToList(), you get another list of references to the exact same objects.
Now to solve your problem. Just create a new list with new objects with something like
var newList = oldList.Select(x => new Model(x)).ToList();
And of course the Model class has a constructor that accept a Model as a parameter and copy the properties.
When you write _events = events;, you create not a new object, but a reference for the same object. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/index .
You should to clone (create a copy of object itself) as it mentioned in comment by #Matt.
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);
This is in someway related to this (Getting all unique Items in a C# list) question.
The above question is talking about a simple array of values though. I have an object returned from a third party web service:
public class X
{
public Enum y {get; set;}
}
I have a List of these objects List<x> data;, about 100 records in total but variable. Now I want all the possible values in the list of the property y and I want to bind this do a CheckBoxList.DataSource (in case that makes a difference).
Hows the most efficient way to do this?
I can think of two algorithms:
var data = HashSet<Enum> hashSet = new HashSet<Enum>(xs.Select(s => s.y));
chkBoxList.DataSource = data;
Or
var data = xs.Select(s => s.y).Distinct();
chkBoxList.DataSource = data;
My gut feeling is the HashSet but I'm not 100% sure.
Open to better ideas if anyone has any idea?
If it is a one time operation - use .Distinct. If you are going to add elements again and again - use HashSet
The HashSet one, since it keeps the objects around after the hashset object has been constructed, and foreach-ing it will not require expensive operations.
On the other hand, the Distinct enumerator will likely be evaluated every time the DataSource is enumerated, and all the work of removing duplicate values will be repeated.
I have a small collection (about 10 items) that each has a medium collection (from 200 to sometimes 500) of items linked (Many-to-one relationship, if I'm correct).
I am looking for a way to iterate through the lists like this:
var cardSetQry = from cs in mDb.CARD_SET
select cs;
List<CARD_SET> listCardSets = cardSetQry.ToList();
Dictionary<string, List<CARD>> cardsList = new Dictionary<string, List<CARD>>();
foreach (var cardSet in listCardSets)
{
CARD_SET set = cardSet;
var cardQry = from c in mDb.CARD
where c.CARD_SET_IDE == set.CARD_SET_IDE
select c;
if (cardQry.Any())
{
HashSet<CARD> listCards = new HashSet<CARD>(cardQry.ToList());
foreach (var card in listCards)
{
card.CARD_MASTER_IDE = null;
card.CARD_CHILD_IDE = null;
mDb.SaveChanges();
}
cardsList.Add(set.CARD_SET_NAME, listCards);
}
}
I'm using the dictionary because I need both the card set name and the list of objects related, but I'm open to suggestions. This method is called once when the app is started and has to iterate through every items.
As for the second list (HashSet), I've started using this since last week. Small research made me believe that a hashSet is a fast collection type. But I don't know how it works.
So, the question: to achieve what I need to do, what would be the fastest collections to use, and why?
It's doubtful that you'll see a significant difference in the speed of enumerating a HashSet<T> versus enumerating a List<T>, regardless of the size.
Which you use should depend on how you're going to use it. If you want the ability to quickly determine if something is in the collection, then use HashSet. Otherwise, use List. HashSet has its uses, but as a simple sequential list, it's overkill. It also uses 3 or 4 times as much memory as does List.
Also, what's the point of doing cardQry.ToList() when creating your HashSet? You can just write new HashSet<CARD>(cardQry).
In such a small collection speed is not an issue. You should use Dictionary if you really want to map it as key value. Otherwise just use Hashset.Comparison of Hashset list and dictionary