Linq Group by Values in a list - c#

So Currently I have this TrackingInfo class which contains an ID and a list of EmailActionIDs, which is an int.
I have a List Of this class which the data looks like:
ID, | EmailActions
_______________
A | 1, 3, 5
B | 3, 5, 6
C | 2, 4, 6
I'm trying to write a Linq Statement To convert this into A list of IDs grouped by each individual value in the list.
So the Results Set would look like:
ID | Values
_______________
1 | A
2 | C
3 | A, B
4 | C,
5 | A, B
6 | B, C
I can't figure out how I would write the group by can anyone give me some insight.
DistinctValues = new List<int> {1,2,3,4,5,6};
TrackingInfo.Groupby(t => DistinctValues.foreach(d =>
t.EmailActions.Contains(d))).Tolist()
This ofcourse isn't working any suggestions on how to do this using Linq

Its easy enough to get a distinct list of EmailActions
var distinctEmailActions = items.SelectMany(i => i.EmailActions).Distinct();
Then pivoting this is a little complex, but here it is:
var result = distinctEmailActions.Select(e => new {
ID=e,
Values = items.Where(i => i.EmailActions.Contains(e)).Select(i => i.ID)
});
Live example: http://rextester.com/CQFDY66608

What you're looking for is SelectMany, but it's easier to use query syntax here:
var result = from item in source
from action in item.EmailActions
group item.ID by action into g
select new { ID = g.Key, Values = g.ToList() }

You can do it by first generating a range using Enumerable.Range, and then matching EmailActions, like this:
var res = Enumerable.Range(1, 6)
.SelectMany(v => TrackingInfo.Where(info => info.EmailActions.Contains(v)).Select(info => new { Id, Value = v }))
.GroupBy(p => p.Value)
.Select(g => new {
Id = g.Key
, Values = g.Select(p => p.Id).ToList()
});

You can achieve this using SelectMany & GroupBy like this:-
var result = tracking.SelectMany(x => x.EmailActionIDs,
(trackObj, EmailIds) => new { trackObj, EmailIds })
.GroupBy(x => x.EmailIds)
.Select(x => new
{
ID = x.Key,
Values = String.Join(",", x.Select(z => z.trackObj.ID))
}).OrderBy(x => x.ID);
Working Fiddle.

Related

EF Core header-detail query optimization

I have a header-detail relation in my SQL Server database. I have around 10k headers, each of them having 1-1k details. And the number of unique elements is about 1k.
Elements [id]
1
2
3
Headers [id]
1
2
3
Details [id, header_id, element_id]
1 1 1
2 1 2
3 1 3
4 2 1
5 3 1
It's very easy to query a list of headers with their details with such structure:
var searchHeaderIds = new List<int>{1,2,3};
var headers = context.Headers
.Where(h => searchHeaderIds.Contains(h.Id))
.Include(h => h.Details)
.ToList();
But what I want to query is a list of elements (1-200) where every element has a list of headers it belongs to (something like an inversion). I can write it in C# as below:
var searchElementIds = new List<int>{1,2,3};
var headers = context.Details
.Where(d => searchElementIds.Contains(d.element_id))
.GroupBy(d => d.element_id)
.Select(g => new {
id = g.Key,
header_ids = g.Select(x => x.header_id) })
.ToList();
But I wonder, what will be the fastest way to do it using the power of SQL/EF?
UPD: I'm ready to use extra data structures, preprocess the data in the database, or do anything else to improve performance.
what about:
var searchElementIds = new List<int>{1,2,3};
var headers = (
from header in context.Headers
join detail in context.Details on header.id equals detail.header_id
where searchElementIds.Contains(detail.element_id)
select header).Distinct();
If you want instances of the Element class:
var headers =
context.Details
.Where(d => searchElementIds.Contains(d.element_id))
.GroupBy(d => d.element_id)
.Select(g => new Element
{
id = g.Key,
header_ids = g.Select(x => x.header_id
})
.ToList();
Don't cal ToList() in the middle of your query.
This is most optimal query in your case. It is closer to original post, but reduced number of retrieved fields for intermediate result:
var headers = context.Details
.Where(d => searchElementIds.Contains(d.element_id))
.Select(d => new { d.element_id, d.header_id })
.ToList() // we need this, EF do not support retrieving grouping detals
.GroupBy(d => d.element_id)
.Select(g => new Element
{
id = g.Key,
header_ids = g.Select(x => x.header_id).ToList()
})
.ToList();

sum distinct values from a column in datagridview

I have a datagridview with two columns like this:
group | quantity
------------------------
chest | 3
legs | 7
back | 2
chest | 1
back | 5
legs | 2
What I'm trying to do is to get the sum of distinct group to a list and use that list for populate another datagridview.
So the result must be in this example:
chest | 4
legs | 9
back | 7
I've tried some linq query code but without any success.
How can I do it?
Here's some Linq queries I tried:
List<string> vv = dataGridView1.Rows.Cast<DataGridViewRow>()
.Where(x => !x.IsNewRow)
// either..
.Where(x => x.Cells[7].Value != null)
//..or or both
.Select(x => x.Cells[7].Value.ToString())
.Distinct()
.ToList();
dataGridView6.DataSource = vv;
EDIT
the group column is being auto filled after a selection of another column combobox, the quantity is filled manually. For the group by I found this code and works but throw an error if a cell is empty:
var Sums = dataGridView1.Rows.Cast<DataGridViewRow>()
.GroupBy(row => row.Cells[7].Value.ToString()) // group column
.Select(g => new { User = g.Key, Sum = g.Sum(row => Convert.ToInt32(row.Cells[1].Value)) });
dataGridView6.DataSource = Sums.ToList();
ok, here the solution that works:
var Sums = dataGridView1.Rows.Cast<DataGridViewRow>()
.Where(row => row.Cells[7].Value != null)
.GroupBy(row => row.Cells[7].Value.ToString()) // group column
.Select(g => new { User = g.Key, Sum = g.Sum(row => Convert.ToInt32(row.Cells[1].Value)) }); // quantity column
dataGridView6.DataSource = Sums.ToList();

LINQ: Group By, and retrieve common property

I have the following dataset, which is stored in a variable called list1.
countrypk | countryname | statepk | statename
---------------------------------------------
1 USA 1 New York
1 USA 2 California
2 Canada 3 Manitoba
I want to be able to group by countrypk, and retrieve the country name.
I have the following LINQ that achieves that effect, but was wondering if there was a better or more straight forward way to do it in LINQ.
var finalList = list1
.GroupBy(item => item.countrypk)
.Where(item => item.Count() > 0)
.Select(item => item.First())
The desired output is:
countrypk | countryname
---------------------------------------------
1 USA
2 Canada
The addition of the Where is not needed. If you have a group it contains at least a single item in it. You can do something like this:
list1.GroupBy(item => item.countrypk)
.Select(item => new { item.Key, item.First().countryname} );
Or using a different overload of GroupBy:
list1.GroupBy(item => item.countrypk,
selector => new { selector.countrypk, selector.countryname} )
.Select(group => group.First())
If you grouped by countrypk, your result set wouldn't have duplicates in it. Your desired result set has duplicate countrypk values in it (1). To get your desired result set, do this:
var finalList = list1.Select(s => new { s.countrypk, s.countryname });
Edit: nevermind this part above, OP editted the question.
I want to be able to group by countrypk, and retrieve the country name What you're asking for is different than what your result set shows. If you want a map of countrypk to country name using your list1, here's one way to do it:
var finalList = list1
.GroupBy(g => new { g.countrypk, g.countryname })
.ToDictionary(k => k.Key.countrypk, v => v.Key.countryname);
Note that you don't need GroupBy to do this. Here's another solution:
var finalList = list1
.Select(s => new { s.countrypk, s.countryname })
.Distinct()
.ToDictionary(k => k.countrypk, v => v.countryname);
In either case, to get the country name for id 1, do this:
var countryName = finalList[1];
Try the following
var finalList = list1
.GroupBy(item => item.countrypk)
.Select(g => new { countrypk = g.Key, countryname = g.First().countryname });
which should provided the desired output
Basically you want to remove duplicates by countrypk and select only first two columns? Use this extension:
public static IEnumerable<TSource> DistinctBy<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
HashSet<TResult> set = new HashSet<TResult>();
foreach(var item in source)
{
var selectedValue = selector(item);
if (set.Add(selectedValue))
yield return item;
}
}
And then
var finalList = list1
.DistinctBy(item => item.countrypk)
.Select(item=> new {item.countrypk, item.countryname })
.ToList();

Get top n rows and sum the rest and call it others in Entity Framework linq lambda query

My data structure:
BrowserName(Name) Count(Y)
MSIE9 7
MSIE10 8
Chrome 10
Safari 11
-- and so on------
What I'm trying to do is get the top 10 and then get the sum of rest and call it 'others'.
I'm trying to get the others as below but geting error..
Data.OrderBy(o => o.count).Skip(10)
.Select(r => new downModel { modelname = "Others", count = r.Sum(w => w.count) }).ToList();
The error is at 'r.Sum(w => w.count)' and it says
downModel does not contain a definition of Sum
The downModel just has string 'modelname' and int 'count'.
Any help is sincerely appreciated.
Thanks
It should be possible to get the whole result - the top ten and the accumulated "others" - in a single database query like so:
var downModelList = context.Data
.OrderByDescending(d => d.Count)
.Take(10)
.Select(d => new
{
Name = d.Name,
Count = d.Count
})
.Concat(context.Data
.OrderByDescending(d => d.Count)
.Skip(10)
.Select(d => new
{
Name = "Others",
Count = d.Count
}))
.GroupBy(x => x.Name)
.Select(g => new downModel
{
modelName = g.Key,
count = g.Sum(x => x.Count)
})
.ToList();
If you want to create just one model, then get the sum first and create your object:
var count = Data.OrderBy(o => o.count).Skip(10).Sum(x => x.count);
var model = new downModel { modelname = "Others", count = count };
Btw, OrderBy performs a sort in ascending order. If you want to get (or Skip) top results you need to use OrderByDescending.

Remove Duplicate based on column value-linq

i have many to many relationship between employee and group. following linq statement
int[] GroupIDs = {6,7};
var result = from g in umGroups
join empGroup in umEmployeeGroups on g.GroupID equals empGroup.GroupID
where GroupIDs.Contains(g.GroupID)
select new { GrpId = g.GroupID,EmployeeID = empGroup.EmployeeID };
returns groupid and the employeeid. and result is
GrpId | EmployeeID
6 | 18
6 | 20
7 | 19
7 | 20
I need to remove the rows for which the employeeid is repeating e.g. any one of the row with employeeid= 20
Thanks
Okay, if you don't care which employee is removed, you could try something like:
var result = query.GroupBy(x => x.EmployeeId)
.Select(group => group.First());
You haven't specified whether this is in LINQ to SQL, LINQ to Objects or something else... I don't know what the SQL translation of this would be. If you're dealing with a relatively small amount of data you could always force this last bit to be in-process:
var result = query.AsEnumerable()
.GroupBy(x => x.EmployeeId)
.Select(group => group.First());
At that point you could actually use MoreLINQ which has a handy DistinctBy method:
var result = query.AsEnumerable()
.DistinctBy(x => x.EmployeeId);

Categories

Resources