There is a lot of data in the database, and it is necessary to produce statistics (find the average number of each operation per day by each user of the application) using c # collections. In my opinion, it is necessary to use dictionaries:
var dict = new Dictionary<long?, Dictionary<DateTime, Dictionary<OperationsGroupType, int>>>();
Please advise a more practical way to write it. As it looks strange. Thank you
I wrote a function:
public void D()
{
var dict = new Dictionary<long?, Dictionary<DateTime, Dictionary<OperationsGroupType, int>>>();
int pageNumber = 0;
int pageSize = 5;
int pageCount = 1;
while (pageNumber < pageCount)
{
int count;
foreach (OperationData op in OperationService.GetPage(pageNumber, pageSize, out count))
if(op.PerformedBy.HasValue)
if(op.PerformedDate.HasValue)
if (dict.ContainsKey(op.PerformedBy))
if (dict[op.PerformedBy].ContainsKey(op.PerformedDate.Value.Date.Date))
if (dict[op.PerformedBy][op.PerformedDate.Value.Date.Date.Date.Date].ContainsKey(op.Type)) dict[op.PerformedBy][op.PerformedDate.Value.Date.Date.Date.Date][op.Type]++;
else dict[op.PerformedBy][op.PerformedDate.Value.Date.Date.Date.Date].Add(op.Type, 1);
else dict[op.PerformedBy].Add(op.PerformedDate.Value.Date.Date.Date.Date, new Dictionary<OperationsGroupType, int> { { op.Type, 1 } });
else dict.Add(op.PerformedBy, new Dictionary<DateTime, Dictionary<OperationsGroupType, int>> { { op.PerformedDate.Value.Date.Date.Date.Date, new Dictionary<OperationsGroupType, int> { { op.Type, 1 } } } });
pageCount = (count - 1) / pageSize + 1;
pageNumber++;
}
foreach (var item in dict)
{
var opDateDict = new Dictionary<DateTime, int>();
foreach (var operDate in item.Value) opDateDict.Add(operDate.Key, operDate.Value.Sum(count => count.Value));
SystemLogger.Instance.WriteErrorTrace(String.Format("Average number of user operations {0} per day: {1}\n", item.Key, opDateDict.Values.Sum() / opDateDict.Count));
}
}
OperationsGroupType - this enum
Please tell me how to replace the dictionary with a more practical design? Which pattern is best for solving this problem?
It's terribly difficult to say what's best or most practical - and that's because you didn't really define what you mean by "best" or "practical".
I'm going to define them as minimal code and minimal repetition.
To start with I created these extension methods:
public static class Ex
{
public static R Ensure<T, R>(this Dictionary<T, R> @this, T key) where R : new
{
if (@this.ContainsKey(key))
return @this[key];
else
{
var r = new R();
@this[key] = r;
return r;
}
}
public static R Ensure<T, R>(this Dictionary<T, R> @this, T key, Func<R> factory)
{
if (@this.ContainsKey(key))
return @this[key];
else
{
var r = factory();
@this[key] = r;
return r;
}
}
}
With those I can rewrite you code like this:
foreach (OperationData op in OperationService.GetPage(pageNumber, pageSize, out count))
{
if (op.PerformedBy.HasValue)
if (op.PerformedDate.HasValue)
{
dict.Ensure(op.PerformedBy).Ensure(op.PerformedDate.Value.Date).Ensure(op.Type, () => 0);
dict[op.PerformedBy][op.PerformedDate.Value.Date][op.Type]++;
}
}