c# linq query aggregation data with other column value - c#

this is my environment.
I have a datatable with this kind of structure
IDstring | Attrib1 | Attrib2 | Attrib3 | Attrib4 | Value
I need to export average, max, min value group by attrib elements, and I use following code:
var queryTable = from rows in dataTable.AsEnumerable()
group rows by new
{
Attrib1 = rows["Attrib1"],
Attrib2 = rows["Attrib2"],
Attrib3 = rows["Attrib3"],
Attrib4 = rows["Attrib4"]
} into grp
select new
{
Attrib1 = grp.Key.Attrib1,
Attrib2 = grp.Key.Attrib2,
Attrib3 = grp.Key.Attrib4,
Attrib4 = grp.Key.Attrib14,
Avg = grp.Average(s => Convert.ToDouble(s["Value"])),
Min = grp.Min(s => Convert.ToDouble(s["Value"])),
Max = grp.Max(s => Convert.ToDouble(s["Value"])),
Count = grp.Count()
};
If I need also to export one IDstring (not necessary all occurrence) that match max and min value, how can I do it?
I have tried with another linq query for every max and min element of upon querytable results to the original datatable but it is too slow for the number of elements I have. Can you help me please?

With the big help of coder of code, this could be the solution:
First I make on order by Value and then i take first and last element.
var queryTable = from rows in resultTable.AsEnumerable()
orderby rows.Field<double>("Value")
group rows by new
{
Attrib1 = rows["Attrib1"],
Attrib2 = rows["Attrib2"],
Attrib3 = rows["Attrib3"],
Attrib4 = rows["Attrib4"]
} into grp
select new
{
Attrib1 = grp.Key.Attrib1,
Attrib2 = grp.Key.Attrib2,
Attrib3 = grp.Key.Attrib3,
Attrib4 = grp.Key.Attrib4,
Avg = grp.Average(s => Convert.ToDouble(s["Value"])),
Min = grp.Min(s => Convert.ToDouble(s["Value"])),
IDStringMin = grp.First().Field<string>("IDstring"),
Max = grp.Max(s => Convert.ToDouble(s["Value"])),
IDStringMax = grp.Last().Field<string>("IDstring"),
Count = grp.Count()
};

Related

How to obtain a column total for values in a list - double of anonymous type

I have a database that contains a column of filtered double values that I am trying to get the total of. I have no problem building a collection of values but can't calculate the value combined total.
My Query
var query2 = from r in db.Clocks
where r.WkNo == Current_Wk
group r by r.DailyHrs into g
select new
{
Value = g.Sum( item => item.DailyHrs)
};
var MyList = (query2.ToList());
//The resulting list
//Value = 0.00
//Value = 9.25
//Value = 8.00
How can I determine the total value? i.e 0.00 + 9.25 + 8.00
I have looked at many posts here but can't get it to work.
"firstly materialize the query with .ToList() before calling .Sum():"
[Reference] https://stackoverflow.com/a/20338921/8049376
var query2 = (from r in db.Clocks
where r.WkNo == clock.WkNo
group r by r.DailyHrs into g
select new
{
Value = g.Sum( item => item.DailyHrs)
}).ToList();
var sum = query2.Sum(t => t.Value);

Listing after implementing ranking skipping numbers

I am trying to achieve ranking functionality as below:
Name Points rank
ram 9 1
kamal 9 1
preet 8 2
lucky 7 3
kishan 6.5 4
devansh 6 5
neha 6 5
I have used below code to achieve this:
finalResult = finalResult.OrderByDescending(i => i.points).ThenBy(i => i.academy).ToList();
finalResult = finalResult.AsEnumerable() // Client-side from here on
.Select((player, index) => new RankingEntity()
{
competitorid = player.competitorid,
firstname = player.firstname,
lastname = player.lastname,
academy = player.academy,
points = player.points,
place = player.place,
eventId = player.eventId,
eventname = player.eventname,
categoryname = player.categoryname,
Rank = index + 1
}).ToList();
var t = (from i in finalResult
let rank = finalResult.First(x => x.points == i.points)
select new
{
Col1 = i,
Rank = rank.Rank
}).ToList();
List<RankingEntity> ttt = new List<RankingEntity>();
foreach (var item in t)
{
var a = item.Col1;
var row = new RankingEntity();
row.competitorid = a.competitorid;
row.firstname = a.firstname;
row.lastname = a.lastname;
row.academy = a.academy;
row.points = a.points;
row.place = a.place;
row.eventId = a.eventId;
row.eventname = a.eventname;
row.categoryname = a.categoryname;
row.Rank = item.Rank;
ttt.Add(row);
}
And i am getting result like below:
Please help what i am doing wrong.
What you are trying to achieve is a ranking of a "group" so group the results by the points and then order the groups. For each item in the group give the same rank.
finalResult.GroupBy(item => item.Points) // Group by points
.OrderDescendingBy(g => g.Key) // Order the groups
.Select((g, index) => new { Data = g, GroupRank = index + 1}) // Rank each group
.SelectMany(g => g.Data.Select(item => new RankingEntity
{
/* properties of each item */
Rank = g.GroupIndex
}); // Flatten groups and set for each item the group's ranking
The problem in your method is that you give the ranking for individual items and not the group. Then when you retrieve the rank for the group (from i in finalResult let rank = finalResult.First(x => x.points == i.points)...) you actually set for each item in the group the ranking of one of the elements in it. Therefore, if you first got the last item of the group - that will be the Rank value of each item in it.
Also notice that in the first line of your code you use ToList. Therefore there is not need to use AsEnumerable in the line under it - it is already a materialized in memory collection.

Linq Group By multiple fields and Flatten the list

I've got the following data
title | useful
ttitle1 | Yes
ttitle1 | Yes
ttitle1 | No
ttitle2 | Yes
I would like to group the above data and flatten it so I get the following result:
Title | Useful Count | Not Useful Count
tttitle1 | 2 | 1
tttitle2 | 1 | 0
Tried this, but it does not produce the correct result:
var query = (from r in ratings
group r by new { r.ArticleTitle, r.Useful } into results
group results by new { results.Key.ArticleTitle } into results2
from result in results2
select new
{
Title = result.Key.ArticleTitle,
Yes = result.Select(i => i.Useful).Count(),
No = result.Select(i => i.Useful == false).Count()
});
Any help?
It seems to me that the only problem is that you're grouping twice. I'd expect this to work:
var query = from rating in ratings
group rating by rating.ArticleTitle into g
select new
{
Title = g.Key,
Yes = g.Count(r => r.Useful),
No = g.Count(r => !r.Useful)
};
Or not in query expression form:
var query = ratings.GroupBy(r => r.ArticleTitle,
(key, rs) => new
{
Title = key,
Yes = rs.Count(r => r.Useful),
No = rs.Count(r => !r.Useful)
});
You don't need to group twice to get the desired result. One Grouping would be fine:
var query = (from r in ratings
group r by new { r.ArticleTitle } into g
from result in groups
select new
{
Title = result.Key,
Yes = result.Select(i => i.Useful).Count(),
No = result.Select(i => !i.Useful).Count()
});

LINQ sum not performed when values are identical

I need to sum elements of same type starting from 2 LINQ queries.
Below is my code:
var query1 = from d in _contextProvider.Context.Documents
where d.TransportId == transportId
group d by d.Type
into dg
select new { DocumentType = dg.Key.ToString(), DocumentCount = dg.Count() };
var query2 = from n in _contextProvider.Context.NotificationDocuments
where n.TransportId == transportId
group n by n.TransportId
into nd
select new { DocumentType = "Notification", DocumentCount = nd.Count() };
var query_collapsed = query1.Union(query2)
.GroupBy(p => new { DocumentType = p.DocumentType })
.Select(g => new DocumentCounters() { DocumentType = g.Key.DocumentType, DocumentCount = g.Sum(p => p.DocumentCount) });
Example: below let's analyse values for DocumentType equals to Notification.
Values of query1:
Values of query2:
The collapsed query :
That's correct: 1 + 2 = 3
The problem: I noticed that whenever the count for Notification in query1 is equals to the count for Notification in query2, then the sum is not performed.
Example:
2 + 2 = 2
or
3 + 3 = 3
Any ideas ?
LINQ Union will remove duplicate entries. If you want to merge the two sequences you can use Concat like so:
var query_collapsed = query1.Concat(query2)
.GroupBy(p => new { DocumentType = p.DocumentType })
.Select(g => new DocumentCounters() { DocumentType = g.Key.DocumentType, DocumentCount = g.Sum(p => p.DocumentCount) });

Linq Transpose List

I have list of simple objects:
var r = new List
{
new { Id = 1, Value = 2, DateTime = DateTime.Parse("10.10.2014")},
new { Id = 2, Value = 3, DateTime = DateTime.Parse("10.10.2014")},
new { Id = 3, Value = 4, DateTime = DateTime.Parse("10.10.2014")},
new { Id = 1, Value = 5, DateTime = DateTime.Parse("11.10.2014")},
new { Id = 2, Value = 6, DateTime = DateTime.Parse("11.10.2014")}
};
I want to get object like:
DateTime | 1 | 2 | 3 |
10.10.2014 | 2 | 3 | 4 |
11.10.2014 | 5 | 6 | |
Is there any nice linq query to this? Smth like pivot/unpivot in sql maybe?
Try this:
r.ToLookup(t => t.id, t=>t.DateTime)
And if that doesn't work, read through this
You are looking to group the list according to id and then key the resulting list by dictionary. You should be able to do that with some combination of GroupBy, which creates a list of grouped lists and ToDictionary(), which creates allows you to specify a property of the object as a key and creates a dictionary from it.
You're looking for a simple GroupBy:
var result = r.GroupBy(x => x.DateTime)
.Select (grp => new
{
DateTime = grp.Key,
_1 = grp.Where(x => x.Id == 1).Select(x => x.Value).Cast<Int32?>().FirstOrDefault(),
_2 = grp.Where(x => x.Id == 2).Select(x => x.Value).Cast<Int32?>().FirstOrDefault(),
_3 = grp.Where(x => x.Id == 3).Select(x => x.Value).Cast<Int32?>().FirstOrDefault()
});
result is now:
If the number of Ids are not known at compile-time then there is no way to create a link statement to capture those Ids as new fields. Linq just can do that. The best you can do in that case is this:
var ids = r.Select(x => x.Id).Distinct().OrderBy(x => x).ToArray();
var query =
from x in r
group x by x.DateTime into gxs
let lookup = gxs.ToLookup(x => x.Id, x => (int?)x.Value)
select new
{
DateTime = gxs.Key,
Values = ids.Select(i => new
{
Id = i,
Value = lookup[i].FirstOrDefault(),
}).ToArray(),
};
That produces this:
If the Ids are known then the following variation is the best:
var query =
from x in r
group x by x.DateTime into gxs
let lookup = gxs.ToLookup(x => x.Id, x => (int?)x.Value)
select new
{
DateTime = gxs.Key,
_1 = lookup[1].FirstOrDefault(),
_2 = lookup[2].FirstOrDefault(),
_3 = lookup[3].FirstOrDefault(),
};

Categories