I have a data like
public class PermList
{
public int UserId { get; set; }
public int GroupId { get; set; }
public int ModuleId { get; set; }
public int BitMaskedPermission { get; set; }
public List<PermList> TestData()
{
List<PermList> theList = new List<PermList>();
PermList sample1 = new PermList {BitMaskedPermission = 15, GroupId = 3, ModuleId = 2, UserId = 1};
theList.Add(sample1);
PermList sample2 = new PermList { BitMaskedPermission = 2, GroupId = 3, ModuleId = 1, UserId = 1 };
theList.Add(sample2);
PermList sample3 = new PermList { BitMaskedPermission = 48, GroupId = 2, ModuleId = 2, UserId = 1 };
theList.Add(sample3);
return theList;
}
}
I would like to apply OR to BitMaskedPermissions with grouping ModuleId. Here is what I would like to get;
How can I achieve this with using Linq.
TIA.
When you have an aggregation operation to perform that isn't one of the built-in ones (Sum, Max etc), you have to turn to Aggregate, which is more verbose but also much more powerful. Here, you want
var data = TestData();
var grouped =
from permList in data
group permList by new { permList.UserId, permList.ModuleId } into g
select new { // or a named class if you have one
g.Key.UserId,
g.Key.ModuleId,
BitMaskedPermission
= g.Aggregate(0, (acc, curr) => acc | curr.BitMaskedPermission)
};
Here, we pass Aggregate a function which takes the accumulator acc and the current value curr, and bitwise ORs them to get the ongoing accumulator value.
If you prefer the method-chaining syntax, it would look like (courtesy of #Chris):
var grouped = PermList.TestData()
.GroupBy(x=> new{x.UserId, x.ModuleId})
.Select(x=> new {
x.Key.UserId,
x.Key.ModuleId,
mask = x.Aggregate(0, (acc, curr)=>acc|curr.BitMaskedPermission)}
)
Maybe something like this:
PermList p=new PermList();
var result= (
from test in p.TestData()
group test by new{test.UserId,test.ModuleId} into g
select new
{
g.Key.UserId,
g.Key.ModuleId,
BitMaskedPermission= g.Sum (x =>x.BitMaskedPermission )
}
);
Related
I am looking forward to get a linq query for populating list of teachers and their respective divisons.
Here I have 2 classes Teacher and Division which are related by DivisionGroupID - GroupID
public class Teacher
{
public int ID { get; set; }
public string Name { get; set; }
public List<Division> lstDivison {get;set;}
public int DivisionGroupID { get; set; }
}
public class Division
{
public int GroupID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
In main method List of both Teacher and Division will be populated
static void Main(string[] args)
{
Teacher obj = new Teacher { ID = 1, DivisionGroupID = 11, Name = "abcd" };
Teacher obj1 = new Teacher { ID = 2, DivisionGroupID = 12, Name = "efgh" };
List<Teacher> objList = new List<Teacher>();
objList.Add(obj);
objList.Add(obj1);
Division dv = new Division { GroupID = 11 ,Name="Division1",Description="first" };
Division dv1 = new Division { GroupID = 11, Name = "Division2", Description = "second" };
Division dv2 = new Division { GroupID = 11, Name = "Division3", Description = "third" };
Division dv3 = new Division { GroupID = 12, Name = "Division4", Description = "fourth" };
Division dv4 = new Division { GroupID = 12, Name = "Division5", Description = "fifth" };
Division dv5 = new Division { GroupID = 12, Name = "Division6", Description = "sixth" };
List<Division> lstDiv = new List<Division>();
lstDiv.Add(dv);
lstDiv.Add(dv1);
lstDiv.Add(dv2);
lstDiv.Add(dv3);
lstDiv.Add(dv4);
lstDiv.Add(dv5);
}
The requirement here is to get the list of teachers and populate the sublist of divisions each teachers holding. I got the solution based on 2 approaches.
Using sub query approach :
var upd = from teacher in objList
select new Teacher
{
ID = teacher.ID,
Name = teacher.Name,
lstDivison = (from div in lstDiv
where div.GroupID == teacher.DivisionGroupID
select new Division
{
Name = div.Name,
Description = div.Description
}).ToList()
};
Using Foeach loop through Teacher collection(objList) and updating the lstDivision
objList.ForEach(x => x.lstDivison = lstDiv
.Where(y => y.GroupID == x.DivisionGroupID)
.Select(p => new Division { Name = p.Name, Description = p.Description })
.ToList());
Both of these approaches will give me the result. But i am looking forward a better approach in as part of my project requirement which has to improve the query performance. Could you please suggest which is the best approach to handle this situation?
use yours teacher object to populate list of divisions under it. as my understanding that how it was designed class structure.
//populate property in object
objList.ForEach(x => {
x.lstDivison = lstDiv.Where(w=> w.GroupID == x.DivisionGroupID).ToList();
});
objList.Dump();
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);
}
Let's say I have two tables, parent table 'P' and sub-table 'S', I usually wrote the Linq like this to get what I want:
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select new{
Id = p.Id,
Title = p.Title
SubContentCount = subContent.Count()
}
It's very simple, but if for some reason I have to pass a parameter into this query when there has one (let's say 'key'), I have to do this (I guess :-):
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select p;
if(!string.IsNullOrEmpty(key)){ // I'm using C#
rows = rows.Where(q => q.Title.Contains(key))
}
And then:
var list = rows.Select(q => new ()
{
Id = q.Id,
Title = q.Title,
subCount = ???.Count()
});
Is that passable to do Linq like this? if so, how?
Thanks for any kind help!
You could create a method that receives a Func<Table, bool>as parameter and use it to filter your dataset:
public static void Main(string[] args)
{
var rows = new List<Table>
{
new Table { Id = 1, Title = "A", SubContent = new [] { "A1" } },
new Table { Id = 2, Title = "B", SubContent = new [] { "B1", "B2" } },
new Table { Id = 3, Title = "C", SubContent = new [] { "C1", "C2", "C3" } },
};
var title = "C";
foreach (var item in Filter(rows, table =>
String.IsNullOrEmpty(title) || table.Title == title))
{
Console.WriteLine(
"Title={0}, SubContent.Length={1}",
item.Title, item.SubContent.Length);
}
}
public static List<Table> Filter(List<Table> original, Func<Table, bool> filter)
{
return original.Where(filter).ToList();
}
public class Table
{
public int Id { get; set; }
public string Title { get; set; }
public string[] SubContent { get; set; }
}
Why not include the filter in the where clause?
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
Quick example in the interactive console:
public class Parent { public int Id {get; set;} public string Title {get; set;} }
public class SubTable { public int Id {get; set;} public int ParentId {get; set;} }
public class Result { public int Id {get; set;} public string Title {get; set;} public int SubContentCount {get; set;} }
var p1 = new Parent() { Id = 1, Title = "Parent_1" };
var p2 = new Parent() { Id = 2, Title = "Parent_2" };
var p3 = new Parent() { Id = 3, Title = "Parent_3" };
var s1_1 = new SubTable() { Id = 11, ParentId = 1 };
var s1_2 = new SubTable() { Id = 12, ParentId = 1 };
var s1_3 = new SubTable() { Id = 13, ParentId = 1 };
var s2_1 = new SubTable() { Id = 21, ParentId = 2 };
var s2_2 = new SubTable() { Id = 22, ParentId = 2 };
var s3_1 = new SubTable() { Id = 31, ParentId = 3 };
var db_P = new List<Parent>() { p1, p2, p3 };
var db_S = new List<SubTable>() { s1_1, s1_2, s1_3, s2_1, s2_2, s3_1 };
public IEnumerable<Result> GetResults(string key = null)
{
var rows = from p in db_P
join s in db_S on p.Id equals s.ParentId into subContent
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
select new Result() {
Id = p.Id,
Title = p.Title,
SubContentCount = subContent.Count()
};
return rows;
}
And example output (formatted onto multiple lines for readability)
> GetResults().ToList()
List<Submission#0.Result>(3) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" },
Submission#0.Result { Id=2, SubContentCount=2, Title="Parent_2" },
Submission#0.Result { Id=3, SubContentCount=1, Title="Parent_3" }
}
> GetResults("1").ToList()
List<Submission#0.Result>(1) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" }
}
>
I have 2 lists that contain different data but have similar columns.
Basically I want to join these lists but then merge the similar columns into 1.
var listCombo= List1.Where(a=>DataIds.Contains(a.dataId)).DefaultIfEmpty()
.Join(List2,
list1 => list1.key,
list2 => list2.key,
(L1,L2) => new
{
L2.key,
L2.dataId,
L2.dataValue,
L2.date,
L1.secId,
L1.dataId,
L1.dataValue
});
I'd like to combine the dataId and dataValue columns together. How do I do that?
So I can just say listCombo.dataId or listCombo.dataValue rather than having to uniquely name them.
If I understand your question, you're trying to combine the two fields into one. Assuming that you have a Class
public class List1 {
public int dataId {get;set;}
public string dataValue {get;set;}
public string combinedValue {get;set;}
}
No you can use like,
var listCombo= List1.Where(a=>DataIds.Contains(a.dataId)).DefaultIfEmpty()
.Join(List2,
list1 => list1.key,
list2 => list2.key,
(L1,L2) => new List1
{
dataId = L2.dataId,
dataValue = L2.dataValue
combinedValue = L2.dataId + " - " L2.dataValue
});
I would use POCO methods for a list1, list2, and a third list which is a combo of both. I would be sure that if you are combining unlike types you do appropriate casts or if you want complex types you can define them as their own properties or what not.
static void Main(string[] args)
{
List<A> lisA = new List<A> {new A {AId = 1, AName = "Brett"}, new A {AId = 2, AName = "John"}};
List<B> lisB = new List<B> {new B { BId = 1, BName = "Doe" }, new B { BId = 2, BName = "Howard" } };
List<C> lisC = lisA.Join(lisB,
list1 => list1.AId,
list2 => list2.BId,
(L1, L2) => new C
{
CId = L1.AId,
CName = L1.AName + " " + L2.BName
}).ToList();
lisC.ForEach(
n => Console.WriteLine(n.CName + "\n")
);
}
public class A
{
public int AId { get; set; }
public string AName { get; set; }
}
public class B
{
public int BId { get; set; }
public string BName { get; set; }
}
public class C
{
public int CId { get; set; }
public string CName { get; set; }
}
I'm trying to prepare data for a graph using LINQ.
The problem that i cant solve is how to calculate the "difference to previous.
the result I expect is
ID= 1, Date= Now, DiffToPrev= 0;
ID= 1, Date= Now+1, DiffToPrev= 3;
ID= 1, Date= Now+2, DiffToPrev= 7;
ID= 1, Date= Now+3, DiffToPrev= -6;
etc...
Can You help me create such a query ?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public class MyObject
{
public int ID { get; set; }
public DateTime Date { get; set; }
public int Value { get; set; }
}
class Program
{
static void Main()
{
var list = new List<MyObject>
{
new MyObject {ID= 1,Date = DateTime.Now,Value = 5},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(1),Value = 8},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(2),Value = 15},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(3),Value = 9},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(4),Value = 12},
new MyObject {ID= 1,Date = DateTime.Now.AddDays(5),Value = 25},
new MyObject {ID= 2,Date = DateTime.Now,Value = 10},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(1),Value = 7},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(2),Value = 19},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(3),Value = 12},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(4),Value = 15},
new MyObject {ID= 2,Date = DateTime.Now.AddDays(5),Value = 18}
};
Console.WriteLine(list);
Console.ReadLine();
}
}
}
One option (for LINQ to Objects) would be to create your own LINQ operator:
// I don't like this name :(
public static IEnumerable<TResult> SelectWithPrevious<TSource, TResult>
(this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> projection)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
yield break;
}
TSource previous = iterator.Current;
while (iterator.MoveNext())
{
yield return projection(previous, iterator.Current);
previous = iterator.Current;
}
}
}
This enables you to perform your projection using only a single pass of the source sequence, which is always a bonus (imagine running it over a large log file).
Note that it will project a sequence of length n into a sequence of length n-1 - you may want to prepend a "dummy" first element, for example. (Or change the method to include one.)
Here's an example of how you'd use it:
var query = list.SelectWithPrevious((prev, cur) =>
new { ID = cur.ID, Date = cur.Date, DateDiff = (cur.Date - prev.Date).Days) });
Note that this will include the final result of one ID with the first result of the next ID... you may wish to group your sequence by ID first.
Use index to get previous object:
var LinqList = list.Select(
(myObject, index) =>
new {
ID = myObject.ID,
Date = myObject.Date,
Value = myObject.Value,
DiffToPrev = (index > 0 ? myObject.Value - list[index - 1].Value : 0)
}
);
In C#4 you can use the Zip method in order to process two items at a time. Like this:
var list1 = list.Take(list.Count() - 1);
var list2 = list.Skip(1);
var diff = list1.Zip(list2, (item1, item2) => ...);
Modification of Jon Skeet's answer to not skip the first item:
public static IEnumerable<TResult> SelectWithPrev<TSource, TResult>
(this IEnumerable<TSource> source,
Func<TSource, TSource, bool, TResult> projection)
{
using (var iterator = source.GetEnumerator())
{
var isfirst = true;
var previous = default(TSource);
while (iterator.MoveNext())
{
yield return projection(iterator.Current, previous, isfirst);
isfirst = false;
previous = iterator.Current;
}
}
}
A few key differences... passes a third bool parameter to indicate if it is the first element of the enumerable. I also switched the order of the current/previous parameters.
Here's the matching example:
var query = list.SelectWithPrevious((cur, prev, isfirst) =>
new {
ID = cur.ID,
Date = cur.Date,
DateDiff = (isfirst ? cur.Date : cur.Date - prev.Date).Days);
});
Further to Felix Ungman's post above, below is an example of how you can achieve the data you need making use of Zip():
var diffs = list.Skip(1).Zip(list,
(curr, prev) => new { CurrentID = curr.ID, PreviousID = prev.ID, CurrDate = curr.Date, PrevDate = prev.Date, DiffToPrev = curr.Date.Day - prev.Date.Day })
.ToList();
diffs.ForEach(fe => Console.WriteLine(string.Format("Current ID: {0}, Previous ID: {1} Current Date: {2}, Previous Date: {3} Diff: {4}",
fe.CurrentID, fe.PreviousID, fe.CurrDate, fe.PrevDate, fe.DiffToPrev)));
Basically, you are zipping two versions of the same list but the first version (the current list) begins at the 2nd element in the collection, otherwise a difference would always differ the same element, giving a difference of zero.
I hope this makes sense,
Dave
Yet another mod on Jon Skeet's version (thanks for your solution +1). Except this is returning an enumerable of tuples.
public static IEnumerable<Tuple<T, T>> Intermediate<T>(this IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
yield break;
}
T previous = iterator.Current;
while (iterator.MoveNext())
{
yield return new Tuple<T, T>(previous, iterator.Current);
previous = iterator.Current;
}
}
}
This is NOT returning the first because it's about returning the intermediate between items.
use it like:
public class MyObject
{
public int ID { get; set; }
public DateTime Date { get; set; }
public int Value { get; set; }
}
var myObjectList = new List<MyObject>();
// don't forget to order on `Date`
foreach(var deltaItem in myObjectList.Intermediate())
{
var delta = deltaItem.Second.Offset - deltaItem.First.Offset;
// ..
}
OR
var newList = myObjectList.Intermediate().Select(item => item.Second.Date - item.First.Date);
OR (like jon shows)
var newList = myObjectList.Intermediate().Select(item => new
{
ID = item.Second.ID,
Date = item.Second.Date,
DateDiff = (item.Second.Date - item.First.Date).Days
});
Here is the refactored code with C# 7.2 using the readonly struct and the ValueTuple (also struct).
I use Zip() to create (CurrentID, PreviousID, CurrDate, PrevDate, DiffToPrev) tuple of 5 members. It is easily iterated with foreach:
foreach(var (CurrentID, PreviousID, CurrDate, PrevDate, DiffToPrev) in diffs)
The full code:
public readonly struct S
{
public int ID { get; }
public DateTime Date { get; }
public int Value { get; }
public S(S other) => this = other;
public S(int id, DateTime date, int value)
{
ID = id;
Date = date;
Value = value;
}
public static void DumpDiffs(IEnumerable<S> list)
{
// Zip (or compare) list with offset 1 - Skip(1) - vs the original list
// this way the items compared are i[j+1] vs i[j]
// Note: the resulting enumeration will include list.Count-1 items
var diffs = list.Skip(1)
.Zip(list, (curr, prev) =>
(CurrentID: curr.ID, PreviousID: prev.ID,
CurrDate: curr.Date, PrevDate: prev.Date,
DiffToPrev: curr.Date.Day - prev.Date.Day));
foreach(var (CurrentID, PreviousID, CurrDate, PrevDate, DiffToPrev) in diffs)
Console.WriteLine($"Current ID: {CurrentID}, Previous ID: {PreviousID} " +
$"Current Date: {CurrDate}, Previous Date: {PrevDate} " +
$"Diff: {DiffToPrev}");
}
}
Unit test output:
// the list:
// ID Date
// ---------------
// 233 17-Feb-19
// 122 31-Mar-19
// 412 03-Mar-19
// 340 05-May-19
// 920 15-May-19
// CurrentID PreviousID CurrentDate PreviousDate Diff (days)
// ---------------------------------------------------------
// 122 233 31-Mar-19 17-Feb-19 14
// 412 122 03-Mar-19 31-Mar-19 -28
// 340 412 05-May-19 03-Mar-19 2
// 920 340 15-May-19 05-May-19 10
Note: the struct (especially readonly) performance is much better than that of a class.
Thanks #FelixUngman and #DavidHuxtable for their Zip() ideas!