Naphstor
Naphstor

Reputation: 2496

Counting elements with same pattern from a C# list object

Hi I have a C# list object that contains employee data from my sql table. Now there is a column name BadgeNumber which has either 6 character or 13 character string values. I need the count of all the 6 char and 13 char badges.

I used foreach loop to get the count and it is working fine. But I was wondering if there is any faster solution to this?

Below is my current implementation.

var empDetails = GetAllEmployeeDetails();

int internalEmpCount;
int contractEmpCount;

foreach (var emp in empDetails)
{
    if (emp.BadgeNumber.Length == 6)
        internalEmpCount++;
    else if (emp.BadgeNumber.Length == 13)
        contractEmpCount++;
}
Console.WriteLine("{0}, {1}", internalEmpCount, contractEmpCount);

I looked at this solution here but that is more about duplicate values and thus I don't think I can use group by here as in my case all the badge numbers are unique and only similarity is the length of badge numbers. But any suggestions or comments are appreciated!

Upvotes: 0

Views: 860

Answers (3)

Harald Coppoolse
Harald Coppoolse

Reputation: 30512

Your code enumerates your sequence exactly once. To calculate the requested values, the complete sequence needs to be enumerated. Hence you, nor LINQ can't get it more efficient.

The only efficiency you can have is by replacing your two if statements with one switch statement. This way property length will be evaluated only once:

switch (emp.BadgeNumber.Length)
{
    case 6:
        internalEmpCount++;
        break;
    case 13:)
        contractEmpCount++;
        break;
    // default: do nothing
}

Or, if you are absolutely certain that the length is either 6 or 13:

if (emp.BadgeNumber.Length == 6)
    internalEmpCount++;
else
    contractEmpCount++;

More efficiency: Let your database do the counting

One of the slower parts of a database query is the transport of the selected data from the DBMS to your local process. Hence it is wise to limit the amount of transported data.

If the only reason for your list is counting the elements, consider to let your database do the counting and transport only the end result instead of the complete list.

IQueryable<EmpDetail> empDetails = ...

var result = empDetails
    // keep only the empDetail that you want to count:
    .Where(empDetail => empDetail.BadgeNumber.Length == 6
                     || empDetail.BadgeNumber.Length == 13)

    // make groups with same BadgeNumber.Length:
    .GroupBy(empDetail => empDetail.BadgeNumber.Length,

         // ResultSelector:
         (length, empDetailsWithThisLength) => new
         {
             Length = length,
             Count = empDetailsWithThisLength.Count,
         });

Result: a collection of two items, each with [Length, Count]. So only 4 integers will be transported.

Upvotes: 0

Jamiec
Jamiec

Reputation: 136174

You can also group by the badgenumber length. If you rurn the result to a dictionary, you can easily get your required values out.

var empDetails = GetAllEmployeeDetails();
var result = empDetails.GroupBy(x => x.BadgeNumber.Length)
                       .ToDictionary(k => k.Key, v => v.Count());
Console.WriteLine("{0}, {1}", result[6], result[13]);

Note this is not faster, not more efficient, and not any more readable than your solution!

Upvotes: 1

user6f6e65
user6f6e65

Reputation: 146

No, this is okay. However you do not require the 13 length limit check. This is only if you have input validation. You may change it to an else speeding up execution marginally.

Upvotes: 1

Related Questions