Count the Items from a Ienumerable<T> Without Iterating

Count the items from a IEnumerable T without iterating?

IEnumerable doesn't support this. This is by design. IEnumerable uses lazy evaluation to get the elements you ask for just before you need them.

If you want to know the number of items without iterating over them you can use ICollection<T>, it has a Count property.

How should I get the length of an IEnumerable?

If you need to read the number of items in an IEnumerable<T> you have to call the extension method Count, which in general (look at Matthew comment) would internally iterate through the elements of the sequence and it will return you the number of items in the sequence. There isn't any other more immediate way.

If you know that your sequence is an array, you could cast it and read the number of items using the Length property.

No, in later versions there isn't any such method.

For implementation details of Count method, please have a look at here.

The best way to get a count of IEnumerable T

You will have to enumerate to get a count. Other constructs like the List keep a running count.

IEnumerable T .Count() returns 0

Thank you for clarifying that you were filtering the list after materializing it in the Get() method.

Your issue is that LINQ is a view. So if you iterate an IEnumerable twice, it will go through the source items twice, applying any filters, projections, etc. to the source items. This means that by changing the source items, the enumerable will yield different items the second time you iterate through it because those items no longer match the filter.

I would suggest you modify the method to be like this:

private async Task<int> ApproveOrRejectWorkItems(IEnumerable<WorkItem> workItems, int status)
{
var workItemsToBeUpdated = workItems.Count();

foreach (var workItem in workItems.Where(it => it.StatusId == (int)WorkItemStatus.Submitted))
{
workItem.StatusId = status;
}

await _unitOfWork.WorkItemRepository.Update(workItems);

return workItems.Count();
}

Performance of Enumerable Count method in a loop

Calling Count() on an enumerable source will exhaust the enumerable until all elements are enumerated. Calling Count() on an enumerable in the condition-block in a for loop will therefore exhaust the enumerable at every iteration. For example, calling

var numbers = VerboseRange(1, 5);
for (var index = 0; index < numbers.Count(); index++)
{
Console.WriteLine($"For-loop is at index {index}...");
}

IEnumerable<int> VerboseRange(int start, int count)
{
foreach (var number in Enumerable.Range(start, count))
{
Console.WriteLine($"Yielded number {number}.");
yield return number;
}
}

will output

Yielded number 1.
Yielded number 2.
Yielded number 3.
Yielded number 4.
Yielded number 5.
For-loop is at index 0...
Yielded number 1.
Yielded number 2.
Yielded number 3.
Yielded number 4.
Yielded number 5.
For-loop is at index 1...
Yielded number 1.
Yielded number 2.
Yielded number 3.
Yielded number 4.
Yielded number 5.
For-loop is at index 2...
Yielded number 1.
Yielded number 2.
Yielded number 3.
Yielded number 4.
Yielded number 5.
For-loop is at index 3...
Yielded number 1.
Yielded number 2.
Yielded number 3.
Yielded number 4.
Yielded number 5.
For-loop is at index 4...
Yielded number 1.
Yielded number 2.
Yielded number 3.
Yielded number 4.
Yielded number 5.

Therefore, counting before is better.

However, I would recommend you to use a counter and a foreach-loop

var count = 0;
foreach (var item in items)
{
// do something
count++;
}

In C# 7.0, you can finally do

foreach (var (item, index) in items.WithIndex())
{
// do something
}

Performance of IEnumerable T .Count() - difference between iterating over value types and reference types

myEnumerable.Count();

replaced with this:

myEnumerable.Select(el => el.Id).Count();

That definitely fall performance! I don't know how you compare these that conclude it's better but you can easily compare them as follows:

class TestObj
{
public int Id { get; set; }
}

static void Main(string[] args)
{
var dataRef = Enumerable.Empty<TestObj>();
for (int i = 0; i < 100000; i++)
{
dataRef = dataRef.Append(new TestObj { Id = i });
}

var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100000; i++)
{
dataRef.Count();
}
sw.Stop();
Console.WriteLine($"By Ref: {sw.ElapsedMilliseconds / 1000.0} Sec");

sw.Restart();
var dataVal = dataRef.Select(p => p.Id);
for (int i = 0; i < 100000; i++)
{
dataVal.Count();
}
Console.WriteLine($"By Val: {sw.ElapsedMilliseconds / 1000.0} Sec");
}

Results:

By Ref: 0.002 Sec
By Val: 4.111 Sec

Generally ValueType and ReferenceType if we consider them independently as two enumerable, don't have any significant different in iterating time and counting.
But what you did above is adding addition enumeration on reference type enumerable to convert it to value type enumerable and that leads to many overhead and degregation of performance.

If performance is your concern and you're ready to pay its cost by memory, you can use .ToList() and then .Count() will takes near to zero.

How can I count the number of elements in IEnumerator?

Directory.GetFiles(@"C:\yourdir").Length

will give you count directly



Related Topics



Leave a reply



Submit