I have a List
.
How can I count all the elements in this as if it was a single >
List
in the fastest way?
So far I h
I'd like to get the chance to answer this question just to highlight when we should use linq and when a classic for. Unfortunately today people doesn’t care a lot about performance as we got use to work on very powerful computer. Anyway just try the code below and you will discover that Linq is more then 100 times slower than the classic for version. You should use Linq only when the expression you need to write is really complex and you want make it more readable. I didn't spend time to the solution shoed below as I'd like to focus on the performance
public static void Main(string [] arg)
{
//create the list
List<List<string>> listOfList = new List<List<string>>()
{
new List<string>()
{
"1.1","2.2"
}
,
new List<string>()
{
"2.1","2.2","2.3"
}
};
//stopwatch using Linq
Stopwatch stopwatch=new Stopwatch();
stopwatch.Start();
int totalUsingLinq = listOfList.Sum(x => x.Count);
stopwatch.Stop();
Console.WriteLine("Using Linq:{0}",stopwatch.Elapsed); //00005713
int totalUsingFor = 0;
//stopwatch using classic for
stopwatch.Reset();
stopwatch.Start();
totalUsingFor = 0;
for(int i=0;i<listOfList.Count;i++)
{
var mainItem = listOfList[i];
if(mainItem!=null)
{
totalUsingFor += mainItem.Count;
}
}
stopwatch.Stop();
Console.WriteLine("Using for:{0}", stopwatch.Elapsed); //0000010
}
distinct version using for (just for example). In this case I have create a very "bottleneck" function that does the distinct and it is still faster.
public class Program
{
public static void Main(string[] arg)
{
//create the list
List<List<string>> listOfList = new List<List<string>>()
{
new List<string>()
{
"1.1","2.2","1.1","1.1","2.2","1.1","1.1","2.2","1.1","1.1"
}
,
new List<string>()
{
"2.1","2.2","2.3","2.3","1.1","2.2","1.1","1.1","2.2","1.1","1.1","2.2","1.1","1.1","2.2","1.1","1.1","2.2","1.1"
}
};
//stopwatch using Linq
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
int totalUsingLinq = listOfList.Sum(l => l.Distinct().Count());
stopwatch.Stop();
Console.WriteLine("Using Linq:{0}", stopwatch.Elapsed); //000012150
int totalUsingFor = 0;
//stopwatch using classic for
stopwatch.Reset();
stopwatch.Start();
totalUsingFor = 0;
for (int i = 0; i < listOfList.Count; i++)
{
var mainItem = listOfList[i];
if (mainItem != null)
{
for(int y=0;y<mainItem.Count;y++)
{
if(mainItem[y]!=null)
{
totalUsingFor++;
NullDuplicateItems(y, ref mainItem);
}
}
}
}
stopwatch.Stop();
Console.WriteLine("Using for:{0}", stopwatch.Elapsed); //0009440
}
public static void NullDuplicateItems(int index,ref List<string > list)
{
var item = list[index];
for(int i=index+1;i<list.Count;i++)
{
if(list[i]==item)
{
list[i] = null;
}
}
}
}
By using LINQ, I think your code is good with a bit changes that no need for .ToList()
, just call Count()
extension as the following:
int result = listOfLists.SelectMany(list => list).Distinct().Count();
I would recommend a simple, nested loop with a HashSet if you need to eliminate duplicates between lists. It combines the SelectMany and Distinct operations into the set insertion logic and should be faster since the HashSet has O(1) lookup time. Internally Distinct() may actually use something similar, but this omits the construction of the single list entirely.
var set = new HashSet<T>();
foreach (var list in listOfLists)
{
foreach (var item in list)
{
set.Add(item);
}
}
var result = set.Count;
To count all the elements in all the lists in the list, you could use the aggregating operators:
int count = listOfLists.Sum(l => l.Distinct().Count());