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(),
};
Related
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()
});
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) });
Let's say I have,
class Product
{
public int Id {get; set;}
public string Name {get; set;}
public int Order {get; set;}
}
and my data have,
products[0] = new Product { Id = 1, Name = "P1", Order = 1 };
products[1] = new Product { Id = 1, Name = "P2", Order = 2 };
products[2] = new Product { Id = 1, Name = null, Order = 3 };
products[3] = new Product { Id = 2, Name = "P3", Order = 4 };
products[4] = new Product { Id = 2, Name = null, Order = 5 };
products[5] = new Product { Id = 2, Name = null, Order = 6 };
What I need is the last(order by Order desc) non-nullable value of Name per Product.Id. So my final output will look like,
items[0] = new { Id = 1, Name = "P2"};
items[1] = new { Id = 2, Name = "P3"};
If Id=1, I have 3 Names (P1, P2, null) and non-nullable Names (P1, P2) but last one is P3.
This should get the last products in order.
var lastOrders = products
.Where(x => x.Name != null) // Remove inapplicable data
.OrderBy(x => x.Order) // Order by the Order
.GroupBy(x => x.Id) // Group the sorted Products
.Select(x => x.Last()); // Get the last products in the groups
var result = products
.GroupBy(p => p.Id)
.Select(g => g.OrderBy(x => x.Order).Last(x => x.Name != null));
this will give you your desired output:
products.GroupBy(p => p.Id)
.Select(g => g.OrderByDescending(gg => gg.Name)
.Where(gg => gg.Name != null)
.Select(gg => new { gg.Id, gg.Name })
.First());
The task can be solved using the following Linq statement.
var Result = products.OrderBy().Where( null != iProduct.Name ).First();
This requires products to contain at least one item where Name is null, otherwise an Exception will be thrown. Alternatively,
var Result = products.OrderBy().Where( null != iProduct.Name ).FirstOrDefault();
will return null if products contains no such item.
Try with :
var expectedProduct =products.Where(p => p.Id != null).OrderByDescending(p => p.Order).GroupBy(p => p.Id).Last()
I have a table with data similar to below:
Group TimePoint Value
1 0 1
1 0 2
1 0 3
1 1 3
1 1 5
I want to project a table as such:
Group TimePoint AverageValue
1 0 2
1 1 4
EDIT: The data is in a datatable.
Anybody any ideas how this can be done with LINQ or otherwise?
Thanks.
You need to perform Group By
The linq you need is something like:
var query = from item in inputTable
group item by new { Group = item.Group, TimePoint = item.TimePoint } into grouped
select new
{
Group = grouped.Key.Group,
TimePoint = grouped.Key.TimePoint,
AverageValue = grouped.Average(x => x.Value)
} ;
For more Linq samples, I highly recommend the 101 Linq samples page - http://msdn.microsoft.com/en-us/vcsharp/aa336747#avgGrouped
Here's a more function-oriented approach (the way I prefer it). The first line won't compile, so fill it in with your data instead.
var items = new[] { new { Group = 1, TimePoint = 0, Value = 1} ... };
var answer = items.GroupBy(x => new { TimePoint = x.TimePoint, Group = x.Group })
.Select(x => new {
Group = x.Key.Group,
TimePoint = x.Key.TimePoint,
AverageValue = x.Average(y => y.Value),
}
);
You can do:
IEnumerable<MyClass> table = ...
var query = from item in table
group item by new { item.Group, item.TimePoint } into g
select new
{
g.Key.Group,
g.Key.TimePoint,
AverageValue = g.Average(i => i.Value)
};
Assuming a class like this:
public class Record
{
public int Group {get;set;}
public int TimePoint {get;set;}
public int Value {get;set;}
}
var groupAverage = from r in records
group r by new { r.Group, r.TimePoint } into groups
select new
{
Group = groups.Key.Group,
TimePoint = groups.Key.TimePoint,
AverageValue = groups.Average(rec => rec.Value)
};
If I have a List<MyType> as so, with each line representing an item in the collection:
{{ Id = 1, Year = 2010 },
{ Id = 1, Year = 2009 },
{ Id = 1, Year = 2008 },
{ Id = 2, Year = 2010 },
{ Id = 2, Year = 2009 },
{ Id = 2, Year = 2008 }}
I wish to retrieve a collection from this collection of the most recent item for each Id. What will the Linq for this look like?
Desired output:
{{ Id = 1, Year = 2010 },
{ Id = 2, Year = 2010 }}
I have a naiive implementation using a second list variable and a foreach loop, but it's inefficient.
//naiive implementation "p-code"
//...
var mostRecentItems = new List<MyType>();
var ids = collection.Select(i => i.Id).Distinct();
foreach(var id in ids)
{
mostRecentItems.Add(collection.Where(i => i.Id == id).OrderByDescending().First);
}
return mostRecentItems;
Most simply:
var mostRecentById = from item in list
group item by item.Id into g
select g.OrderByDescending(x => x.Year).First();
Group by id, then select the first item in each group ordered in a descending fashion.
var mostRecentItems = collection.GroupBy( c => c.Id )
.Select( g => g.OrderByDescending( i => i.Year ).First() );
or more simply still:
var result = list
.GroupBy(i => i.Id)
.Select(g => new {Id = g.Key, Year = g.Max(y => y.Year)});