I have list of object that I'm passing to function and inside I want to count them.
When I use IQueryable() it dosen't count them correct, but when I use IEnumerable() it count them Ok.
public int functCount(IQueryable<MyObject> myLisfOfObjects)
{
int numberOfItems = myListOfObjects.Count(o => o.isSet); //isSet is bool property in my object
return numberOfItem;
}
On first call it return correct number of items, but when I change isSet properties in some elements in list it returns the same number as first call.
public int functCount(IEnumerable<MyObject> myLisfOfObjects)
{
int numberOfItems = myListOfObjects.Count(o => o.isSet); //isSet is bool property in my object
return numberOfItem;
}
But when I change my code to this, it returns correct count every time.
Can anyone explain me the difference, and why is this happening?
Since whatever you pass to either of those functions does not cause compilation errors, then it is safe to assume it is an IQueryable. Meaning that .Count() will be reevaluated as a SQL statement and run against the DB (ignoring whatever change you thought you have made in memory).
Calling .Count() on IEnumerable however is calling totally different method which will simply return the count of the in-memory collection.
In other words IEnumerable<T>.Count() and IQueryable<T>.Count() are two completely different methods, that do different things.
Related
I have a generic collection of objects as a property of an object. This data comes from a sql query and api. I want to use the RemoveAt method to remove some of them efficiently. But Visual Studio complains to me that the RemoveAt method is undefined. My intuition is to cast the Collection to a List, giving me access to the RemoveAt method. I only want to cast one time, then use RemoveAt as many times as necessary. I could use the Remove(object) command, but it requires traversing the Collection to look for the object for each call, which is slower than using RemoveAt
Here is what I'm trying:
obj.stuckArray = obj.stuckArray.ToList();
After this line, I have a line of code that looks like this:
obj.stuckArray.RemoveAt(1);
Unfortunately the RemoveAt gets underlined with red and the warning from Visual Studio reads: "ICollection does not contain a definition for 'RemoveAt'"
Is it possible to cast once and RemoveAt multiple? Or is this not possible?
Just do it in three statements instead of two, using a local variable:
var list = obj.stuckArray.ToList();
list.RemoveAt(1);
obj.stuckArray = list;
That way the type of stuckArray doesn't matter as much: you only need to be able to call ToList() on it. The RemoveAt method on List<T> is fine because that's the type of list.
Obviously you want to remove an element from the array and store it back into the original member stuckArray. However as an Icollection has no method RemoveAt defined, you get the error. The method however exists on List<T>.
So do the following instead:
var tmp = obj.stuckArray.ToList();
tmp.RemoveAt(1);
obj.stuckArray = tmp;
However this will traverse the entire collection anyway, as ToList will copy the entire collection into a new one. But I don´t see any way around this in order to delete an element from your array, because an array has no RemoveAt-method.
As per your EDIT: why not just make the Remove after the re-definition of your stuckArray:
var tmp = obj.stuckArray.ToList();
obj.stuckArray = tmp;
Now you can call RemoveAt as often as you want:
((List<MyType>)obj.stuckArray).RemoveAt(1);
((List<MyType>)obj.stuckArray).RemoveAt(1);
((List<MyType>)obj.stuckArray).RemoveAt(1);
Casting this so many times shouldn´t have a big impact on your performance, as obj.stuckArray already is a List<MyType>. RemoveAt on the other hand will have an effect here, as the method will copy the internal array, as you can see at the source-code for RemoveAt:
public void RemoveAt(int index) {
if ((uint)index >= (uint)_size) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
Contract.EndContractBlock();
_size--;
if (index < _size) {
Array.Copy(_items, index + 1, _items, index, _size - index); // here the entire array will be traversed again
}
_items[_size] = default(T);
_version++;
}
So by calling RemoveAt three times, you also copy the internal array three times.
If you have control over the object that exposes the struckArray property,
you can expose stuckArray as ICollection<T>, but inside the object use a List<T> as it's backing field. Then you can add a method that removes an item by it's index:
class MyClass
{
private list<int> _stuckArray; // of course, this doesn't have to be int...
public ICollection<int> StuckArray {get {return _stuckArray;}}
public RemoveFromStuckArray(int index)
{
_stuckArray.RemoveAt(index);
}
}
That will enable you to keep whatever references you already have to the property, and also supply a method to remove items by their indexes efficiently, though I'm not sure that's such a good idea to enable removing items by indexes from an ICollection in the first place.
I noticed something interesting today when I was making changes for a pull request. Below is my code:
public List<MatColor> Colors { get; set; }
public List<Customer> Customers { get; set; }
public ProductViewModel()
{
this.Colors = new List<MatColor>();
this.Customers = new List<Customer>();
var products = this.LoadProductViewList();
this.LoadColorComboBox(products);
this.LoadCustomerComboBox(products);
}
public void LoadColorComboBox(IEnumerable<Product> products)
{
this.Colors.Clear();
this.Colors = products.Select(p => new MatColor()
{ Code = p.ColorCode, Description = p.ColorDescription })
.DistinctBy(p => m.Code).DistinctBy(p => p.Description).ToList();
}
private void LoadCustomerComboBox(IEnumerable<Product> products)
{
this.Customers.Clear();
this.Customers = products.Select(p => new Customer()
{ Name = p.CustomerName, Number = p.CustomerNumber })
.DistinctBy(p => p.Number).DistinctBy(p => p.Name).ToList();
}
This code does everything I want it to. It successfully populates both the Colors and Customers lists. I understand why it would always successfully populate the Colors list. That's because LoadColorComboBox(...) gets called first.
But an IEnumerable<T> can only get enumerated, ONCE, right? So once it gets enumerated in LoadColorComboBox(...), how is it successfully getting reset and thus enumerated again in LoadCustomerComboBox(...)? I've already checked the underlying type being returned by LoadProductViewList() -- it calls a REST service which returns a Task<IEnumerable<Product>>. Is my IEnumerable<Product> somehow getting passed as a value? It's not a primitive so I was under the impression it's a reference type, thus, would get passed in by reference as default, which would cause the second method to blow up. Can someone please tell me what I'm not seeing here?
And IEnumerable is an object with a GetEnumerator method, that is able to get you an enumerator. Generally one would expect it to be able to give you any number of enumerators. Some specific implementations might not support it, but the contract of the interface is that it should be able to give you as many as you want.
As for the IEnumerator instances that it spits out, they do technically have a Reset method, which is supposed to set it back to the "start" of the sequence. In practice however, most implementations tend to not support the "reset" operator and just throw an exception when you call the method.
In your case, you're not actually reseting an IEnumerator and trying to use it to get the values of a sequence twice (which wouldn't work, as none of the iterators from the LINQ methods that you're using to create your sequence support being reset). What you're doing is simply getting multiple different enumerators, which those LINQ methods all support.
But an IEnumerable can only get enumerated, ONCE, right?
No. An IEnumerable<T> can be enumerated any number of times. But for each enumeration, each item can be yielded only once.
In your case, products will be enumerated once for each LINQ query that needs the list of products... so once in LoadColorComboBox and once in LoadCustomerComboBox.
In my code I have an IEnumerable:
IEnumerable<SomeType> listOfSomething = MethodWhichReturnsAnIEnumerable(param 1)
Now, each element in listOfSomething, also contains a list of something else let's call it listOfRules. I need to return the elements in listOfSomething which have >0 elements in their listOfRules:
var result = listOfSomething.Where(x => x.listOfRules.Count > 0);
What does that mean for the performance? listOfRules is a List so I'm curious to what calling Count will do to the IEnumerable listOfSomething in terms of whether it will put everything into memory.
Since listOfRules is List, querying Count property is very fast, because for List it just returns the value of private field and not iterating the whole collection each time. Here is an implementation, taken from here:
// Read-only property describing how many elements are in the List.
public int Count {
get {
Contract.Ensures(Contract.Result<int>() >= 0);
return _size;
}
}
If the listOfRules is a List<T> using Count will just return the stored value it won't enumerate the collection. It has nothing to do with listOfSomething, listOfSomething will be enumerated and the Count property will be called on each list.So there is nothing to worry about.
list.Count just returns the value of a field so is very fast. O(1)
So your overall performance will be O(N) where N is number of records in listOfSomething.
I want to invoke Queryable.Where() and get all elements. There's no version of Where() that works without a predicate function. So I have to right this:
var result = table.Where( x => true );
and it works but that feels really stupid to me - x is never used, and there's no "transformation" for the => "arrow" symbol.
Is there a more elegant solution?
You can use the following, which is more elegant:
var result = table;
You could also omit result completely, and use table directly.
Isn't table.Where(x=>true) essentially a noop? I mean, what is the point? You can do use _ instead of x though, which is idiomatic.
table.Where(_=> true);
But really, the following is what you are doing:
for (var item in table)
{
if (true) // your Where() clause..
{
yield item;
}
}
See how it doesn't really make sense?
table.Where( x => true ) is not "returning all elements". It simply returns an enumerable that has enough information to return some subset of elements when it is being enumerated upon. Until you enumerate it, no elements are "returned".
And since this subset is not even proper in this case (i.e. all elements are returned), this is essentially a no-op.
To enumerate over all elements, write a simple foreach, or use ToList or ToArray or if you don't care about actually returning any elements (and just want to enumerate, presumably for side-effects): table.All(x => true) or table.Any(x => false), or even just table.Count().
In this case you would not need to call Where because you are not filtering the Queryable.
If you still wish to call Where and you do this in many places you could define a static Func and reuse that:
public static Func<int, bool> t = ReturnTrue;
public static bool ReturnTrue(int i)
{
return true;
}
table.Where(t);
If you're trying to get a copy of the contents of table instead of a reference,
var result = table.ToList();
but it's not clear if that's really what you're trying to accomplish. Details?
I wanted to generate a unique identifier for the results of a Linq query i did on some date.
Initially i thought of using Guid for that but stumbling upon this problem i had to improvise.
However I'd like to see if anyone could have a solution using Guid so here we go.
Imagine we have:
class Query
{
public class Entry
{
public string Id { get; set; }
public int Value { get; set; }
}
public static IEnumerable<Entry> GetEntries( IEnumerable<int> list)
{
var result =
from i in list
select new Entry
{
Id = System.Guid.NewGuid().ToString("N"),
Value = i
};
return result;
}
}
Now we want Id to be unique for each entry, but we need this value to be the same for each traversal of the IEnumerable we get from GetEntries. This means that we want calling the following code:
List<int> list = new List<int> { 1, 2, 3, 4, 5 };
IEnumerable<Query.Entry> entries = Query.GetEntries(list);
Console.WriteLine("first pass");
foreach (var e in entries) { Console.WriteLine("{0} {1}", e.Value, e.Id); }
Console.WriteLine("second pass");
foreach (var e in entries) { Console.WriteLine("{0} {1}", e.Value, e.Id); }
to give us something like:
first pass
1 47f4a21a037c4ac98a336903ca9df15b
2 f339409bde22487e921e9063e016b717
3 8f41e0da06d84a58a61226a05e12e519
4 013cddf287da46cc919bab224eae9ee0
5 6df157da4e404b3a8309a55de8a95740
second pass
1 47f4a21a037c4ac98a336903ca9df15b
2 f339409bde22487e921e9063e016b717
3 8f41e0da06d84a58a61226a05e12e519
4 013cddf287da46cc919bab224eae9ee0
5 6df157da4e404b3a8309a55de8a95740
However we get:
first pass
1 47f4a21a037c4ac98a336903ca9df15b
2 f339409bde22487e921e9063e016b717
3 8f41e0da06d84a58a61226a05e12e519
4 013cddf287da46cc919bab224eae9ee0
5 6df157da4e404b3a8309a55de8a95740
second pass
1 a9433568e75f4f209c688962ee4da577
2 2d643f4b58b946ba9d02b7ba81064274
3 2ffbcca569fb450b9a8a38872a9fce5f
4 04000e5dfad340c1887ede0119faa16b
5 73a11e06e087408fbe1909f509f08d03
Now taking a second look at my code above I realized where my error was:
The assignment of Id to Guid.NewGuid().ToString("N") gets called every time we traverse the collection and thus is different everytime.
So what should i do then?
Is there a way i can reassure that i will get with only one copy of the collection everytime?
Is there a way that i'm sure that i won't be getting the new instances of the result of the query?
Thank you for your time in advance :)
This is a inherent to all LINQ queries. Being repeatable is coincidental, not guaranteed.
You can solve it with a .ToList() , like:
IEnumerable<Query.Entry> entries = Query.GetEntries(list).ToList();
Or better, move the .ToList() inside GetEntries()
Perhaps you need to produce the list of entries once, and return the same list each time in GetEntries.
Edit:
Ah no, you get each time the different list! Well, then it depends on what you want to get. If you want to get the same Id for each specific Value, maybe in different lists, you need to cache Ids: you should have a Dictionary<int, Guid> where you'll store the already allocated GUIDs. If you want your GUIDs be unique for each source list, you would perhaps need to cache the input the return IEnumerables, and always check if this input list was already returned or not.
Edit:
If you don't want to share the same GUIDs for different runs of GetEntries, you should just "materialize" the query (replacing return result; with return result.ToList();, for example), as it was suggested in the comment to your question.
Otherwise the query will run each time you traverse your list. This is what is called lazy evaluation. The lazy evaluation is usually not a problem, but in your case it leads to recalculating the GUID each query run (i.e., each loop over the result sequence).
Any reason you have to use LINQ? The following seems to work for me:
public static IEnumerable<Entry> GetEntries(IEnumerable<int> list)
{
List<Entry> results = new List<Entry>();
foreach (int i in list)
{
results.Add(new Entry() { Id = Guid.NewGuid().ToString("N"), Value = i });
}
return results;
}
That's because of the way linq works. When you return just the linq query, it is executed every time you enumerate over it. Therefore, for each list item Guid.NewGuid will be executed as many times as you enumerate over the query.
Try adding an item to the list after you iterated once over the query and you will see, that when iterating a second time, the just added list item will be also in the result set. That's because the linq query holds an instance of your list and not an independent copy.
To get always the same result, return an array or list instead of the linq query, so change the return line of the GetEntries method to something like that:
return result.ToArray();
This forces immediate execution, which also happens only once.
Best Regards,
Oliver Hanappi
You might think not using Guid, at least not with "new".
Using GetHashCode() returns unique values that don't change when you traverse the list multiple times.
The problem is that your list is IEnumerable<int>, so the hash code of each item coincides with its value.
You should re-evaluate your approach and use a different strategy. One thing that comes into my mind is to use a pseudo-random number generator initialized with the hash code of the collection. It will return you always the same numbers as soon as it's initialized with the same value. But, again, forget Guid
One suggestion: (Don't know if that's your case or not though)
If you want to save the entries in database, Try to assign your entry's primary key a Guid at the database level. This way, each entry will have a unique and persisted Guid as its primary key. Checkout this link for more info.