Aggregate function over an aggregate result set using linq - linq

I have the following linq query:
var totalAmountsPerMonth =
from s in Reports()
where s.ReportDate.Value.Year == year
group s by s. ReportDate.Value.Month into g
orderby g.Key
select new
{
month = g.Key,
totalRecaudacion = g.Sum(rec => rec.RECAUDACION),
totalServicios = g.Sum(ser => ser.SERVICIOS)
};
var final = new ResultSet
{
Recaudacion = meses.Average(q => q. totalRecaudacion),
Servicios = meses.Average(o => o. totalServicios)
};
And I need to obtain the average of the total amount of “RECAUDACION” and “SERVICIOS” of each month. I made this query. However, I definitely think this is not the best solution at all. Could you please suggest me a better and more efficient approach (in a single query if possible) to get these data?

I have created a simple extension method. And it turns out to be two times more efficient in a simple stopwatch benchmark.
public class Report
{
public DateTime? Date { get; set; }
public int RECAUDACION { get; set; }
public int SERVICIOS { get; set; }
}
static class EnumerableEx
{
public static Tuple<double, double> AveragePerMonth(this IEnumerable<Report> reports)
{
var months = new HashSet<int>();
double RECAUDACION = 0d;
double SERVICIOS = 0d;
foreach (Report rep in reports)
{
if (!months.Contains(rep.Date.Value.Month))
{
months.Add(rep.Date.Value.Month);
}
RECAUDACION += rep.RECAUDACION;
SERVICIOS += rep.SERVICIOS;
}
var totalMonth = months.Count;
if (months.Count > 0)
{
RECAUDACION /= totalMonth;
SERVICIOS /= totalMonth;
}
return Tuple.Create<double, double>(RECAUDACION, SERVICIOS);
}
}

Related

how to add the number of items at the end of the linq query

i have the following LINQ
public IEnumerable<TurbineStatus> turStatus()
{
var result = (from s in _db.masterData
group s by s.current_turbine_status into g
select new TurbineStatus
{
status = g.Key,
numberOfTurbines = g.Count()
}
).ToList().OrderByDescending(s => s.status);
return result;
}
my class:
public class TurbineStatus
{
public string status { get; set; }
public int numberOfTurbines { get; set; }
public int allTurbines { get; set; }
}
i gives me the number of turbines according to the status,for example, 5turbines STOP,6 turbines RUN,10 turbines Link Down and so on,i also need to have sum of all these statuses,in my TurbinesStatus class i have a field which is int and named it allTurbines,how can i achieve it?
It's probably better for performance reasons to let the DB handle sorting and then once the result is materialized, you can extract the sum of the turbines count:
var result =
(
from s in _db.masterData
group s by s.current_turbine_status into g
select new TurbineStatus
{
status = g.Key,
numberOfTurbines = g.Count()
} into statusGroup
orderby statusGroup.status descending
select statusGroup
).ToList();
int totalTurbines = groups.Sum(statusGroup => statusGroup.numberOfTurbines);
To return both as a TurbinesStatus instance:
public class TurbinesStatus
{
public IReadOnlyList<TurbineStatus> TurbineStatuses { get; }
public int TotalCount { get; }
public TurbinesStatus(IReadOnlyList<TurbineStatus> turbineStatuses)
{
TurbineStatuses = turbineStatuses;
TotalCount = turbineStatuses.Sum(ts => ts.numberOfTurbines);
}
}
and then
public TurbinesStatus turStatus()
{
var statuses =
(
from s in _db.masterData
group s by s.current_turbine_status into g
select new TurbineStatus
{
status = g.Key,
numberOfTurbines = g.Count()
} into statusGroup
orderby statusGroup.status descending
).ToList();
return new TurbinesStatus(statuses);
}

how to practically assign repeating objects from groups

I am having a difficult time finding a proper Linq query to utilize the group output.
I want to populate an existing students List where Student class has 2 properties ID and and int[] Repeats array (can be a list too) to keep how many times they took any of the 4 lectures (L101,L201,L202,L203). So if student takes L101 twice, L202 and L203 once, and but didn't take L201 this should be {2,0,1,1,}
class Student{
public string ID{get;set;}
public int[] Repeats{get;set;} //int[0]->L101, int[1]->L201...
}
In my main class I do this basic operation for this task:
foreach (var student in students)
{
var countL101 = from s in rawData
where student.Id==s.Id & s.Lecture =="L101"
select; //do for each lecture
student.Repeats = new int[4];
student.Repeats[0] = countL101.Count(); //do for each lecture
}
This works; but I wonder how do you make it practically using Linq in case where there are 100s of lectures?
I am using Lamba Expressions rather than query syntax. Then assuming rawData is IEnumerable<T> where T looks something like...
class DataRow
{
/// <summary>
/// Id of Student taking lecture
/// </summary>
public string Id { get; set; }
public string Lecture { get; set;}
}
Then you could do something like...
var lectures = rawData.Select(x => x.Lecture).Distinct().ToList();
int i = 0;
lectures.ForEach(l =>
{
students.ForEach(s =>
{
if (s.Repeats == null)
s.Repeats = new int[lectures.Count];
s.Repeats[i] = rawData.Count(x => x.Id == s.Id && x.Lecture == l);
});
i++;
});
Now if Repeats could just be of type IList<int> instead of int[] then...
var lectures = rawData.Select(x => x.Lecture).Distinct().ToList();
lectures.ForEach(l =>
{
students.ForEach(s =>
{
if (s.Repeats == null)
s.Repeats = new List<int>();
s.Repeats.Add(rawData.Count(x => x.Id == s.Id && x.Lecture == l));
});
});
Things are further simplified if Repeats could just be instantiated to a new List<int> in the Student constructor...
class Student
{
public Student()
{
Repeats = new List<int>();
}
public string Id { get; set; }
public IList<int> Repeats { get; private set; }
}
Then you can do it in one line...
rawData.Select(x => x.Lecture).Distinct().ToList()
.ForEach(l =>
{
students.ForEach(s =>
{
s.Repeats.Add(rawData.Count(x => x.Id == s.Id && x.Lecture == l));
});
});

Get Count from entity framework

Quite new to EF, basically i want to convert this SQL query:
SELECT
PSKU.ProductSKUID,
PSKU.ProductSKUName,
W.WarehouseID,
W.WarehouseName,
SA.SystemAreaName,
COUNT(SLI.ProductSKUID) AS QTY
FROM dbo.StockLineItem AS SLI INNER JOIN
dbo.ProductSKU AS PSKU ON PSKU.ProductSKUID = SLI.ProductSKUID INNER JOIN
dbo.Warehouse AS W ON W.WarehouseID = SLI.WarehouseID INNER JOIN
dbo.SystemArea AS SA ON SA.SystemAreaID = SLI.SystemAreaID
WHERE (SA.SystemAreaID = 1)
AND W.WarehouseID = #WarehouseID
GROUP BY PSKU.ProductSKUID, PSKU.ProductSKUName, W.WarehouseName, SA.SystemAreaName, W.WarehouseID
To an effective EF statement. This is what i Have so far, my Model class and the method:
[Serializable]
public class StockReturnMethod
{
public int ProductSKUID { get; set; }
public int WarehouseID { get; set; }
public int LotID { get; set; }
public string LotName { get; set; }
public int AreaID { get; set; }
public string AreaName { get; set; }
public int BinID { get; set; }
public string BinName { get; set; }
}
public class DALStockMovement
{
scmEntitiesPrimaryCon entities = new scmEntitiesPrimaryCon();
public List<AvailibleStock> AvailibleStockQty(int warehouseID)
{
var rows = (from PLA in entities.ProductLocationAssignments
from W in entities.Warehouses
from SLI in entities.StockLineItems
from SA in entities.SystemAreas
from PSKU in entities.ProductSKUs
where W.WarehouseID == warehouseID
select new AvailibleStock() { WarehouseID = W.WarehouseID, ProductSKUID = PSKU.ProductSKUID, ProductSKUName = PSKU.ProductSKUName, WarehouseName = W.WarehouseName, Status = SA.SystemAreaName, QtyUnassigned = SLI.ProductSKUID }).ToList();
return rows;
}
Any Advice to get this to an Effective EF Statement would be appreciated
I actually used this tool called Linqer, since I had the SQL
I just popped it into that tool and it generated the Linq for me.
Here is what came out:
var SKUStock = (from sli in entities.StockLineItems
where
sli.SystemArea.SystemAreaID == 1 &&
sli.WarehouseID == warehouseID
group new { sli.ProductSKU, sli.Warehouse, sli.SystemArea, sli } by new
{
ProductSKUID = (System.Int32?)sli.ProductSKU.ProductSKUID,
sli.ProductSKU.ProductSKUName,
sli.Warehouse.WarehouseName,
sli.SystemArea.SystemAreaName,
WarehouseID = (System.Int32?)sli.Warehouse.WarehouseID
} into g
select new AvailibleStock()
{
ProductSKUID = (int)(System.Int32?)g.Key.ProductSKUID,
ProductSKUName = g.Key.ProductSKUName,
WarehouseID = (int)(System.Int32?)g.Key.WarehouseID,
WarehouseName = g.Key.WarehouseName,
Status = g.Key.SystemAreaName,
QtyUnassigned = (int)(Int64?)g.Count(p => p.sli.ProductSKUID != null)
}).ToList();
return SKUStock;
It returns exactly what i need :).

linq count/groupby not working

I want to group by the categoryid and then do a count on this. But I don't know how to do this. I have tried a couple of ways without success. Here is my latest:
public class Count
{
public int TradersCount { get; set; }
public int Id { get; set; }
public string Description { get; set; }
}
public IQueryable<Count> CountTradersAttachedToCategories()
{
var data = from tc in _db.tblTradersCategories
select new Count
{
Description = tc.tblCategory.description,
Id = tc.tblCategory.categoryId,
TradersCount = tc.Select(x => x.categoryid).GroupBy().Count()
};
return data;
}
tblTradersCategories joins both
tblTraders/tblCategories
A single trader can have many categories
A single category can have many traders
Thanks in advance for any help.
Clare
Try this:
var data = from tc in _db.tblTradersCategories
group tc by new { tc.tblCategory.categoryId,
tc.tblCategory.description } into g
select new { Count = g.Count(),
Id = g.Key.categoryId,
Description = g.Key.description };
If you want that in your Count class you may need to use AsEnumerable() to perform the conversion in process:
var converted = data.AsEnumerable()
.Select(c => new Count { TradersCount = c.Count,
Id = c.Id,
Description = c.Description });
You can try doing them all in one go:
var data = from tc in _db.tblTradersCategories
group tc by new { tc.tblCategory.categoryId,
tc.tblCategory.description } into g
select new Count { TradersCount = g.Count,()
Id = g.Key.categoryId,
Description = g.Key.description };
But I don't know if that will work. It depends on how the LINQ provider handles it.

Help with linq query. many to many

I have a collection(people) that has a many-to-many reference to another collection(dogs). Suspend your disbelief on how there can be more than one people per dog. People just contains member which is an List<Dog>
I would like to select all the people where the people have a certain property(specified in an IList) and pets have a certain property(specified in an IList).
E.g. I have an IList (used for this query only) with the dog’s property value.
public enum EnumLikesToBite
{
No,
Yes,
Sometimes
}
IList <<EnumLikesToBite>> listDogsMayBite =
{ { EnumLikesToBite.Yes},
{ EnumLikesToBite.Sometimes}};
Then another list for the peoples property:
public enum EnumKeepsPetWith
{
Chain,
String,
Rubberband
}
IList <EnumKeepsPetWith> listPeopleWhoDontRestrainDog =
{ { EnumKeepsPetWith.String },
{ EnumKeepsPetWith.Rubberband}};
How can I query out all the people who have a dog that may bite and don’t restrain dog.
Like this pseudo code:
Var result = from p in People where p.KeepsPet in listPeopleWhoDontRestrainDog and dog.LikesToBite in listDogsMayBite.
Result has all the people. Of course if I could get all the dogs who may bite under those people that would be great.
List<int> mayBite = new List<int>()
{
(int) EnumLikesToBite.Yes,
(int) EnumLikesToBite.Maybe
}
List<int> poorRestraint = new List<int>()
{
(int) EnumKeepsPetWith.String,
(int) EnumKeepsPetWith.RubberBand
}
IQueryable<Person> query =
from p in db.People
where poorRestraint.Contains(p.KeepsPetWith)
where p.DogPeople.Any(dp => mayBite.Contains(dp.Dog.DoesBite))
select p;
var query =
from p in db.People
where poorRestraint.Contains(p.KeepsPetWith)
let bitingDogs =
from dp in p.DogPeople
let d = dp.Dog
where mayBite.Contains(d.DoesBite)
where bitingDogs.Any()
select new {Person = p, BitingDogs = bitingDogs.ToList()};
Maybe this code will help.. One of the possible solution are:
var result =
peoples.Where(y => dontRestrainDog.Contains(y.KeepsPetWith) && y.Dogs.Any(x => dogsMayBite.Contains(x.LikesToBite))).ToList();
result.ForEach(y => y.Dogs = y.Dogs.Where(x => dogsMayBite.Contains(x.LikesToBite)).ToList());
which you can see an example of here:
class Program
{
static void Main(string[] args)
{
IList<EnumLikesToBite> dogsMayBite = new List<EnumLikesToBite>
{
{ EnumLikesToBite.Yes }, { EnumLikesToBite.Sometimes }
};
IList<EnumKeepsPetWith> dontRestrainDog = new List<EnumKeepsPetWith>
{
{ EnumKeepsPetWith.String }, { EnumKeepsPetWith.Rubberband }
};
var peoples = new List<People>();
var dogs = new List<Dog>();
Random gen = new Random(2);
for(int i = 0; i < 10; i++)
{
People p = new People
{
PeopleId = i,
KeepsPetWith = (EnumKeepsPetWith) (gen.Next(10)%3),
Dogs = new List<Dog>()
};
Dog d = new Dog
{
DogId = i,
LikesToBite = (EnumLikesToBite) (gen.Next(10)%3),
Peoples = new List<People>()
};
peoples.Add(p);
dogs.Add(d);
}
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 10; j++)
{
if (gen.Next(10)>7)
{
peoples[i].Dogs.Add(dogs[j]);
}
if (gen.Next(10)>7)
{
dogs[i].Peoples.Add(peoples[j]);
}
}
}
PrintDogs(dogs);
PrintPeoples(peoples);
var result =
peoples.Where(y => dontRestrainDog.Contains(y.KeepsPetWith) && y.Dogs.Any(x => dogsMayBite.Contains(x.LikesToBite))).ToList();
result.ForEach(y => y.Dogs = y.Dogs.Where(x => dogsMayBite.Contains(x.LikesToBite)).ToList());
Console.WriteLine("===================");
PrintPeoples(result);
Console.ReadLine();
}
private static void PrintPeoples(List<People> peoples)
{
Console.WriteLine("=Peoples=");
foreach (var people in peoples)
{
Console.WriteLine("Id: {0}", people.PeopleId);
Console.WriteLine("KeepsPetWith: {0}", people.KeepsPetWith);
Console.WriteLine("Dogs: ");
foreach (var dog in people.Dogs)
{
Console.Write("{0}, ", dog.DogId);
}
Console.WriteLine();
}
}
private static void PrintDogs(List<Dog> dogs)
{
Console.WriteLine("=Dogs=");
foreach (var dog in dogs)
{
Console.WriteLine("Id: {0}", dog.DogId);
Console.WriteLine("LikesToBite: {0}", dog.LikesToBite);
Console.WriteLine("Peoples: ");
foreach (var people in dog.Peoples)
{
Console.Write("{0}, ", people.PeopleId);
}
Console.WriteLine();
}
}
}
public class People
{
public int PeopleId { get; set; }
public EnumKeepsPetWith KeepsPetWith { get; set; }
public IList<Dog> Dogs { get; set; }
}
public class Dog
{
public int DogId { get; set; }
public EnumLikesToBite LikesToBite { get; set; }
public IList<People> Peoples { get; set; }
}
public enum EnumLikesToBite
{
No,
Yes,
Sometimes
}
public enum EnumKeepsPetWith
{
Chain,
String,
Rubberband
}

Resources