Linq: Select 1st item from other group - linq

I am trying to write a query to find just one item from the group, where they grouped by Industry and Weight, then from this one I have to get where Weight is Max and Balance is Max too
This is the example:
var data = new[] {
new {ID = 1, Industry = 2, Weight = 2, Balance = 500},
new {ID = 2, Industry = 2, Weight = 2, Balance = 300},
new {ID = 3, Industry = 2, Weight = 1, Balance = 100},
new {ID = 5, Industry = 4, Weight = 1, Balance = 100},
new {ID = 6, Industry = 4, Weight = 2, Balance = 150},
new {ID = 7, Industry = 4, Weight = 1, Balance = 300},
};
var res = from a in data group a by new {a.Industry, a.Weight} into g
let ID = g.First().ID
let Balance = g.Max(a => a.Balance)
select new { ID, g.Key.Industry, g.Key.Weight, Balance};
Console.WriteLine(res);
So as the result I should get just two records
ID Industry Weight Balance
1 2 2 500
6 4 2 150
but with query above I got 4 records
Any advice?
Regards,
Dmitriy

There are probably many different solutions, but one would be to only group by Industry and sort each group according to your "first element to pick" criteria and then pick the first item in each group.
var res = from item in data
group item by item.Industry into g
let first = g.OrderByDescending(x => x.Weight)
.ThenByDescending(x => x.Balance).First()
select first;

data.GroupBy(x=>new {x.Industry,x.Weight})
.ToDictionary(y=>y.Key,y=>y.ToList().Max(x=>x.Balance));
If you dont want a dictionary then you can as well Select a new DTO or a dynamic object as follows:
data.GroupBy(x=>new {x.Industry,x.Weight})
.Select(x=>new {x.Key.Industry,x.Key.Weight,x.ToList().Max(y=>y.Balance)});
Hopefully that is what you need.

Related

How can I bind group by query to DataGridview in linq?

I want bind the below query to DataGridview.
how can I it?
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numberGroups =
from num in numbers
group num by num % 5 into numGroup
select new { Remainder = numGroup.Key, Numbers = numGroup };
How do you want it represented in the DGV since Numbers could contain more than one item how would this go into a DGV row?
Alternatively this should work:
var numberGroups = from num in numbers
group num by num % 5 into numGroup
select new { Remainder = numGroup.Key, Count = numGroup.Count() };
yourGridView.DataSource = numberGroups.ToList();
yourGridView.DataBind(); //don't use if WinForm DGV

Dynamic linq - Group by interval (DateTime, Numeric)

I search everywhere and didn`t find anwser for this question. I want to group by intervals (DateTime, Numeric) in Dynamic linq (the data will be crated dynamically so i must use dynamic linq)
Lets assume that we have such data:
ID|Date|Price
1|2010-11-01|100
2|2010-11-01|120
3|2010-11-02|50
4|2010-12-01|30
5|2010-12-01|220
6|2011-01-01|400
How to get this data grouped by like this
-(Group by Day) following groups
->2010-11-01 = 2 elements
->2010-11-02 = 1 elements
->2010-12-01 = 2 elements
->2011-01-01 = 1 elements
-(Group by Month) following groups
->2010-11 = 3 elements
->2010-12 = 2 elements
->2011-01 = 1 elements
-(Group by Quarter) following groups
->2010 q.03 = 5 elements
->2011 q.01 = 1 elements
-(Group by Year) following groups
->2010 = 5 elements
->2011 = 1 element
-(Group by Price (From 0, Each 50)) following groups
-> <0-50) = 1 elements
-> <50-100) = 1 elements
-> <100-150) = 2 elements
-> <200-250) = 1 elements
-> <400-450) = 1 elements
-(ideally it would be Group by Price (From 0-50,From 50-150, From 150-500)) following groups
-> <0-50) = 1 elements
-> <50-150) = 3 elements
-> <150-500) = 2 elements
Any Ideas? I stress again - it must be DYNAMIC LINQ or eventually some sophisticated lambda expression? I should been able to "group" it by column name that will be in string. e.g.
GroupBy("Date"), GroupBy("Price");
Here's how to do it:
For instance:
Group by Month
public Item[] data =
{
new Item { Date = new DateTime(2011, 11, 6), Price = 103, Name = "a" },
new Item { Date = new DateTime(2011, 11, 16), Price = 110, Name = "b" },
new Item { Date = new DateTime(2011, 12, 4), Price = 200, Name = "c" },
new Item { Date = new DateTime(2011, 12, 4), Price = 230, Name = "d" },
new Item { Date = new DateTime(2012, 1, 15), Price = 117, Name = "e" }
};
var groups = data.AsQueryable().GroupBy("Date.Month", "it").Cast<IGrouping<int, Item>>();
You can use "Date.Day" and "Date.Year" and for something like a price range you could use a function which maps everything in the range onto the same value e.g. using integer division "(it.Price / 50)"

Linq select subgroup

I have the following in my db table:
- MailingId | GroupName | ServiceId
- 1 | group1 | 3
- 2 | group1 | 5
- 3 | group1 | 8
- 4 | group2 | null
- 5 | group3 | null
...
In my view i have 2 groups of checkboxes:
1) (services) with id's 3,5,8 (serviceId).
2) and a list of checkboxes for mailing groups (group1, group2, group3)
I need to select the following using LINQ:
Select rows that I have selected in ServiceId checkbox list PLUS any other. For example if I check off ServiceId's (3 and 5) and group "Group3" then my output would be rows MailingId: 1, 3 and 5. HOWEVER, if I select ANY service from (first group of checkboxes) AND DO NOT select "Group1" from mailing group checkboxes then rows with Group1 SHOULD NOT be in the output.
I'm using EF4. Please help.
Thanks
The 2 arrays would be the selections that are posted from your view
int[] selectedservices = {3,5};
string[] selectedgroups = {"group3"};
using (Model model = new Model())
{
bool b = selectedservices.Contains(1);
var mailinglists = from m in model.MalingSet
where selectedgroups.Contains(m.GroupName)
&& ((m.ServiceId.HasValue && selectedservices.Contains(m.ServiceId.Value)) || m.ServiceId.HasValue == false)
select m.MailingId;
}
Try something like this.
var mailingGroup =
from m in Mailings
where m.ServiceId != null // or whatever other condition
group m by m.GroupName into g
select new
{
groupName = g.Key,
mailings = g
};
It's a Where clause no?
var a = new {m = 1, g = "group1", sid = 3};
var b = new {m = 2, g = "group1", sid = 5};
var c = new {m = 3, g = "group1", sid = 8};
var d = new {m = 4, g = "group2", sid = 0};
var e = new {m = 5, g = "group3", sid = 0};
var l = new List<dynamic>{a,b,c,d,e};
l.Where(it => ( new ArrayList{3, 5}).Contains(it.sid)
|| (new ArrayList{1, 5}).Contains(it.m)).Dump();
http://www.linqpad.net/

Using LINQ to join [n] collections and find matches

Using LINQ I can find matching elements between two collections like this:
var alpha = new List<int>() { 1, 2, 3, 4, 5 };
var beta = new List<int>() { 1, 3, 5 };
return (from a in alpha
join b in beta on a equals b
select a);
I can increased this to three collections, like so:
var alpha = new List<int>() { 1, 2, 3, 4, 5 };
var beta = new List<int>() { 1, 3, 5 };
var gamma = new List<int>() { 3 };
return (from a in alpha
join b in beta on a equals b
join g in gamma on a equals g
select a);
But how can I construct a LINQ query that will return the matches between N number of collections?
I'm thinking if each collection was added to a parent collection, then the parent collection was iterated through using a recursive loop, it may work?
There's no need to recurse - you can just iterate. However, you may find it best to create a set and intersect that each time:
List<List<int>> collections = ...;
HashSet<int> values = new HashSet<int>(collections[0]);
foreach (var collection in collections.Skip(1)) // Already done the first
{
values.IntersectWith(collection);
}
(Like BrokenGlass, I'm assuming you've got distint values, and that you really just want to find the values which are in all the collections.)
If you prefer the immutable and lazy approach, you could use:
List<List<int>> collections = ...;
IEnumerable<int> values = collections[0];
foreach (var collection in collections.Skip(1)) // Already done the first
{
values = values.Intersect(collection);
}
If you have only unique values you can use Intersect:
var result = alpha.Intersect(beta).Intersect(gamma).ToList();
If you need to preserve multiple values that are not unique you can just exclude non-intersecting items from the original collection as an additional step:
alpha = alpha.Where(x => result.Contains(x)).ToList();
To generalize the Intersect approach you can just use a loop to do all intersections one by one:
IEnumerable<List<int>> collections = new [] { alpha, beta, gamma };
IEnumerable<int> result = collections.First();
foreach (var item in collections.Skip(1))
{
result = result.Intersect(item);
}
result = result.ToList();

Linq group by + where for each group

I want to write a linq expression that will return the ID that does not contain a particular value. For example, I want to return all distinct IDs that do not have Value = 30.
ID, Value
1, 10
1, 20
1, 30
2, 10
2, 20
3, 10
3, 20
The result should be 2 and 3 since non of these have a Value with 30.
Is this possible to do with a single expression?
Thanks
Sure, this will do it:
var query = from i in list
group i by i.GroupId into g
where g.Any(p => p.ItemId == 30) == false
select g.Key;
foreach(var result in query) { Console.WriteLine(result); }
This outputs:
2
3
Here I have used, as an example:
class Product {
public int GroupId { get; set; }
public int ItemId { get; set; }
}
and
var list = new List<Product>() {
new Product() {GroupId = 1, ItemId = 10},
new Product() {GroupId = 1, ItemId = 20},
new Product() {GroupId = 1, ItemId = 30},
new Product() {GroupId = 2, ItemId = 10},
new Product() {GroupId = 2, ItemId = 20},
new Product() {GroupId = 3, ItemId = 10},
new Product() {GroupId = 3, ItemId = 20},
};
I don't have Linq, but here is the SQL Server SQL to do what you want:
DECLARE #YourTable table (ID int, value int)
insert into #YourTable VALUES (1, 10)
insert into #YourTable VALUES (1, 20)
insert into #YourTable VALUES (1, 30)
insert into #YourTable VALUES (2, 10)
insert into #YourTable VALUES (2, 20)
insert into #YourTable VALUES (3, 10)
insert into #YourTable VALUES (3, 20)
SELECT DISTINCT ID
FROM #YourTable y1
WHERE NOT EXISTS (SELECT Value
FROM #YourTable y2
WHERE y1.ID=y2.id and y2.value=30)
OUTPUT:
ID
-----------
2
3
(2 row(s) affected)

Resources