Pivot Table Using Linq - c#

I have a table like this .(VisitType is dynamic)
PersonelId VisitDate VisitTypeId
1 2015-02-24 A
2 2015-02-23 S
2 2015-02-24 D
4 2015-02-22 S
2 2015-02-22 A
2 2015-02-22 B
3 2015-02-23 A
1 2015-02-23 A
1 2015-02-24 D
4 2015-02-24 S
4 2015-02-22 S
2 2015-02-22 S
3 2015-02-24 D
I want to get a pivot of this using linq as below.
VisitDate PersonelId A S D B
2015-02-22 4 0 2 0 0
2015-02-22 2 1 1 0 0
2015-02-23 2 0 1 0 0
2015-02-23 3 1 0 0 0
2015-02-23 1 1 0 0 0
2015-02-24 1 1 0 1 0
2015-02-24 2 0 0 1 0
2015-02-24 4 0 1 0 0
2015-02-24 3 0 0 1 0
I use this linq
var d = (from f in _db.Visits
group f by new {f.VisitDate, f.PersonnelId }
into myGroup
where myGroup.Count() > 0
select new
{
myGroup.Key.VisitDate,
myGroup.Key.PersonnelId,
subject = myGroup.GroupBy(f => f.VisitTypeId).Select
(m => new { Sub = m.Count(), Score = m.Sum(c => c.Amount) })
}).ToList();
It is grouped by date and personel id, but don't count items from every VisitType.

Try this:
//static headers version
var qry = Visits.GroupBy(v=>new{v.VisitDate, v.PersonelId})
.Select(g=>new{
VisitDate = g.Key.VisitDate,
PersonelId = g.Key.PersonelId,
A = g.Where(d=>d.VisitTypeId=="A").Count(),
B = g.Where(d=>d.VisitTypeId=="B").Count(),
D = g.Where(d=>d.VisitTypeId=="D").Count(),
S = g.Where(d=>d.VisitTypeId=="S").Count()
});
//dynamic headers version
var qry = Visits.GroupBy(v=>new{v.VisitDate, v.PersonelId})
.Select(g=>new{
VisitDate = g.Key.VisitDate,
PersonelId = g.Key.PersonelId,
subject = g.GroupBy(f => f.VisitTypeId)
.Select(m => new { Sub = m.Key, Score = m.Count()})
});

first fetch your dynamic Visit Type for person like following
var VisTyp= (from VT in _db.VisitType select new{VisType=VT.VisitType} ).ToList();
then use following.
var d = (from f in _db.Visits
group f by new {f.VisitDate, f.PersonnelId } into myGroup
where myGroup.Count() > 0
select new
{
VisitDate = myGroup.Key.VisitDate,
PersonnelId = myGroup.Key.PersonnelId,
subject=myGroup.Count(t => VisTyp.Contains(t.VisitType))
}).ToList();

Related

Using Linq to GroupBy and Case When via datatable

enter code hereI have a Datatable like this:
LN Test_Code Name Test_Item_Code Test_Data
0107001 A010 Yo V0001 1
0107001 A010 Yo V0002 2
0107001 C040 Yo V0002 1
0107001 C040 Yo V0002 2
0107002 C040 JC V0001 3
0107002 C040 JC V0002 4
0107003 C040 kk V0001 5
0107003 C040 kk V0002 6
I need to get my datatable like this:
LN Test_Code Name DueDate(V0001) DueDate2(V00002)
0107001 A010 Yo 1 2
0107001 C040 Yo 1 2
0107002 C040 JC 3 4
0107003 C040 kk 5 6
This is my SQLcommand if I can you case when.
select LN, Test_Code, Name,
case when Test_Item_Code = 'V0001' then Test_Data else '' end as DueDate(V0001),
case when Test_Item_Code = 'V0002' then Test_Data else '' end as DueDate2(V0002)
From dt Group by LN, Test_Code, Name
This is my source code now. It's still error because I don't know how to get field Test_Item_Code and Test_Data to case when.
DataTable dt = services.getData();
var data = dt.AsEnumerable()
.GroupBy(r1 => new
{LN = r1["LN"],
Test_Code = r1["Test_Code"],
Name = r1["Name"],
Test_Item_Code = r1["Test_Item_Code"],
Test_Data = r1["Test_Data"]
}).Select(g => new
{LN = g.Key.LN,
Test_Code = g.Key.Test_Code,
Name = g.Key.Name,
DueDate = g.Key.Test_Item_Code == "V0001" ? g.Key.Test_Data : "",
DueDate2 = g.Key.Test_Item_Code == "V0002" ? g.Key.Test_Data : ""
});
I got data like this from above code:
LN Test_Code Name DueDate(V0001) DueDate2(V00002)
0107001 A010 Yo 1
0107001 A010 Yo 2
0107001 C040 Yo 1
0107001 C040 Yo 2
0107002 C040 JC 3
0107002 C040 JC 4
0107003 C040 kk 5
0107003 C040 kk 6
Anyone help/advice if possible would be really appreciated. I'm very new to linq.
Solution
var data = dt.Select().GroupBy(r1 => r1["LN"])
.SelectMany(r => r.GroupBy(g => g["Test_Code"]).Select(rr =>
new
{
LN = r.Key,
Test_Code = rr.Key,
Name = rr.First()["Name"],
DueDate = rr.OrderBy(r1 => r1["Test_Item_Code"] == "V0001").First()["Test_Data"],
DueDate2 = rr.OrderBy(r1 => r1["Test_Item_Code"] == "V0002").First()["Test_Data"],
}
));
You need to Group by NL and then by Test_Code, this is an example:
var data = dt.Select().GroupBy(r1 => r1["LN"])
.SelectMany(r => r.GroupBy(g => g["Test_Code"]).Select(rr =>
new
{
LN = r.Key,
Test_Code = rr.Key,
Name = rr.First()["Name"],
DueDate = rr.FirstOrDefault(r1 => r1["Test_Item_Code"] == "V0001")["Test_Data"],
DueDate2 = rr.FirstOrDefault(r1 => r1["Test_Item_Code"] == "V0002")["Test_Data"],
}
));

Linq involving groupby orderby and join

I have two tables
tblEquipment
Id1 Id2 Version1 Version2
1 1 - 0
2 1 A 1
3 1 B 1
4 1 B 2
5 2 - 0
6 2 A 0
and another table
tblHistory
IdParent Version1 Version2 Date
1 - 0 1/01/14
1 A 1 2/01/14
1 B 1 3/01/14
1 B 2 4/01/14
2 - 0 4/01/14
2 A 0 6/01/14
2 A 0 8/01/14
I am trying to write a query that fetches the record which has the maximum Version1 and Version2 corresponding to the maximum version1. For e.g. I want the following records from the above table -
Id2 = 1, Version1 = B, Version2 = 2 and Date = 4/01/14
Id2 = 2, Version1 = A, Version2 = 0 and Date = 8/01/14
Can anyone help me with the linq that gives me the above result.
Fetching data according the rule you described would be like this:
var result = history
.GroupBy( h => h.IdParent )
.Select( h => h.OrderBy( h1 => h1.Version1 )
.ThenBy( h2 => h2.Version2 )
.Last() )
.Select(h => new {
Id2 = h.IdParent,
Version1 = h.Version1,
Version2 = h.Version2,
Date = h.Date
}
);

combinations of 3 values from an array

var example = new[]{1, 1, 1, 0, 1, 1}
I want to produce an array of all arrays that have three 1s in combinations of places that this array does, and 0s in all other positions.
In this example, these will be:
1 1 1 0 0 0
1 1 0 0 1 0
1 1 0 0 0 1
1 0 1 0 1 0
1 0 1 0 0 1
1 0 0 0 1 1
0 1 1 0 1 0
0 1 1 0 0 1
0 1 0 0 1 1
0 0 1 0 1 1
Code I wrote to attempt this became extremely long and messy, there is obviously a better way.
I was trying using 'counters' that sat over positions of the original array, and moved like so:
1 1 1 0 1 1
c c c
c c c
c c c
c c c
etc...
If the array is relatively short (which it should be to avoid overflowing the memory) you could use binary counting to build and algorithm that you are looking for:
Assume that the original array has N elements
Convert the array to an int treating each position as a binary digit; call it set
Make a mask number that counts in a loop from 0 to (1 << N)-1, inclusive. This mask produces all possible combinations of bits that can be taken from the original
Produce a combination of mask and set by AND-ing the two: int candidate = mask & set
Check how many bits are set in the candidate
If the number of set bits is exactly three, add candidate to the list
Once the loop is finished, you have your list represented as ints.
Eliminate duplicates, and convert these ints to arrays of ints as necessary.
Here is a simple implementation of the above algorithm:
static uint FromArray(int[] data) {
uint res = 0;
for (int i = 0 ; i != data.Length ; i++) {
if (data[i] == 1) {
res |= (1U << i);
}
}
return res;
}
static int[] ToArray(uint set, int size) {
var res = new int[size];
for (int i = 0 ; i != size ; i++) {
if ((set & (1U << i)) != 0) {
res[i] = 1;
}
}
return res;
}
static int CountBits(uint set) {
int res = 0;
while (set != 0) {
if ((set & 1) != 0) {
res++;
}
set >>= 1;
}
return res;
}
public static void Main() {
var example = new[]{1, 1, 1, 0, 1, 1};
var set = FromArray(example);
int N = example.Length;
var res = new List<uint>();
for (uint mask = 0 ; mask != 1U<<N ; mask++) {
var candidate = set & mask;
if (CountBits(candidate) == 3) {
res.Add(candidate);
}
}
foreach (var s in res.Distinct()) {
var array = ToArray(s, N);
Console.WriteLine(string.Join(" ", array.Select(i => i.ToString()).ToArray()));
}
}
This code produces the following output:
1 1 1 0 0 0
1 1 0 0 1 0
1 0 1 0 1 0
0 1 1 0 1 0
1 1 0 0 0 1
1 0 1 0 0 1
0 1 1 0 0 1
1 0 0 0 1 1
0 1 0 0 1 1
0 0 1 0 1 1
Demo on ideone.

lambda expression to sort on min values in a group

my Table data is"
ExpenseID PersonID SeqNumb HistorySeqNumb HistoryCode
1 3 1 1 9
2 1 1 1 9
3 2 1 1 0
4 1 2 1 0
5 1 1 2 0
6 5 1 1 0
7 3 1 2 0
ExpenseID is primary Key column .
If a record is inserted for a personID it has a sequence of 1 and History Code indicating 0 is active record.If the record is edited a new row is inserted with current row historyCode changed to 9 and new row History Code 0and history Sequence 2.
If another new record is inserted for the same person it has a new row with incremented sequence number.
My resultSet should contain the active records and the order the records were inserted:
I need lambda expression
Output should be
ExpenseID PersonID SeqNumb HistorySeqNumb HistoryCode
7 3 1 2 0
5 1 1 2 0
3 2 1 1 0
4 1 2 1 0
6 5 1 1 0
Your desired result seems wrong to me...
You only want active records, ok check
The order they were inserted...
3 > 4 > 5 > 6 > 7 not 7 > 5 > 3 > 4 > 6
In the top table Person ID was added again giving it an ExpenseID of 7 which is fine, it should be first (if you are sorting in descending). But then you have PersonID 1 with expense ID 5 when PersonID 5 with expense ID 6 was the previous record added before PersonID 3 with expense id 7
In short, your input data doesn't match your example desired output, making it rather difficult for anyone to answer you here.
From what I can see it looks like they should be sorted where HistoryCode = 0 Orderd By ExpenseID (bad database design comments aside)...
In such case it would be
var sortedItems = rawItems.Where(w => (w.HistoryCode == 0)).ToList();
sortedItems.Sort((emp1, emp2) => emp2.ExpenseID.CompareTo(emp1.ExpenseID));
//where rawItems is a List<Object> where object has properties for ExpenseID, PersonID... etc
Here is the entire Console App I made to experiment with this (bored), Add an Xml file called Data with the used Node/Attributes in it and set it to copy to output directory,
static void Main(string[] args)
{
var rawItems = new[] { new { ExpenseID = 0, PersonID = 0, SeqNumb = 0, HistorySeqNumb = 0, HistoryCode = 0 } }.ToList();
using (FileStream fs = new FileStream(Environment.CurrentDirectory + "\\data.xml", FileMode.Open, FileAccess.Read, FileShare.None))
{
XmlReader reader = XmlReader.Create(fs);
XDocument doc = XDocument.Load(reader);
fs.Flush();
doc.Root.Elements("Item").ToList().ForEach(i =>
{
var xExpenseID = Convert.ToInt32(i.Attribute("ExpenseID").Value);
var xPersonID = Convert.ToInt32(i.Attribute("PersonID").Value);
var xSeqNumb = Convert.ToInt32(i.Attribute("SeqNumb").Value);
var xHistorySeqNumb = Convert.ToInt32(i.Attribute("HistorySeqNumb").Value);
var xHistoryCode = Convert.ToInt32(i.Attribute("HistoryCode").Value);
rawItems.Add(new { ExpenseID = xExpenseID, PersonID = xPersonID, SeqNumb = xSeqNumb, HistorySeqNumb = xHistorySeqNumb, HistoryCode = xHistoryCode });
});
}
//sort
var sortedItems = rawItems.Where(w => (w.HistoryCode == 0)).ToList();
sortedItems.Sort((emp1, emp2) => emp2.ExpenseID.CompareTo(emp1.ExpenseID));
Console.Write("ExpenseID".PadRight(16, ' '));
Console.Write("PersonID".PadRight(16, ' '));
Console.Write("SeqNumb".PadRight(16, ' '));
Console.Write("HistorySeqNumb".PadRight(16, ' '));
Console.WriteLine("HistoryCode".PadRight(16, ' '));
foreach (var item in sortedItems)
{
Console.Write(item.ExpenseID.ToString().PadRight(16, ' '));
Console.Write(item.PersonID.ToString().PadRight(16, ' '));
Console.Write(item.SeqNumb.ToString().PadRight(16, ' '));
Console.Write(item.HistorySeqNumb.ToString().PadRight(16, ' '));
Console.WriteLine(item.HistoryCode.ToString().PadRight(16, ' '));
}
Console.ReadKey(true);
}

Complex merge of one List<CustomType> into another

I need to return a chronological list of alerts. The method 1 below does this.
I also need to grab alerts of a certain alert type such that if the AlertTypeID = {1,2,3,6,8,9,x}, then return those alerts by CreateDate and order them by month.
The method 2 below does this.
Now, I need to insert the results of method 2 into the results of method 1.
Even though the items returned by method 2 are organized by month, we only
care about the most recent. Only the most recent subgroup item from method 2 need to follow the chronological order of the items returned from method 1. This subgroup
item will determine where the rest of the monthly items from subgroup 2 are placed.
A final requirement is that duplicates must be removed. If an item is being returned
from the subgroup alerts, it cannot also exist in the primary group alerts.
Below I have provided an illustration of the effect I am trying to achieve:
december alert 1 (12/23/2012)
december alert 2 (12/21/2012)
december alert 3 (12/20/2012)
december subalert 1 (12/19/2012)
december subalert 2 (12/18/2012)
december subalert 3 (12/04/2012)
december subalert 4 (12/01/2012)
december alert 4 (12/18/2012)
december alert 5 (12/12/2012)
november alert 1 (11/22/2012)
november alert 2 (11/16/2012)
november subalert 1 (11/14/2012)
november subalert 2 (11/08/2012)
november alert 3 (11/12/2012)
Code:
All Alerts By DateTime
List<Alert> result = new List<Alert>();
using(NeuroLabLinqDataContext dc = conn.GetContext())
{
IEnumerable<Alert> alerts = (from a in dc.Alerts
where a.AccountID == AccountID
orderby a.CreateDate descending
select a).Take(40);
result = alerts.ToList();
}
return result;
Alert Types By Month
List<Alert> result = new List<Alert>();
int[] alertTypes = {1,2,3,4,5,6,7,8,9};
using (NeuroLabLinqDataContext dc = conn.GetContext())
{
IEnumerable<Alert> alerts = (from a in dc.Alerts
where a.AccountID == AccountID &&
alertTypes.Contains(a.AlertTypeID)
orderby ((DateTime)a.CreateDate).Month ascending
select a).ToList();
}
return result;
The grouping for the final select statement should look something like this:
select new { Date = alerts.CreateDate,
Message = alerts.Message,
Type = alert.AlertTypeID,
RecentActivity = [if from method 2, then true] };
UPDATE: Updated Method
public List<Alert> GetAlertsByAccountID(Int32 AccountID, params int[] alertTypes)
{
List<Alert> result = new List<Alert>();
using (NeuroLabLinqDataContext dc = conn.GetContext())
{
var all = (from a in dc.Alerts
where a.AccountID == AccountID
orderby a.CreateDate descending
select a);
int abc = all.Count();
var first = all
.Where(a => a.AccountID == AccountID) && !alertTypes.Contains(a.AlertTypeID))
.OrderByDescending(a => a.CreateDate)
.GroupBy(a => a.CreateDate.Date)
.ToDictionary(g => g.Key);
var firstKeys = first.Keys.Cast<DateTime>()
.ToList().OrderBy(k => k);
var second = all
.Where(a => a.AccountID == AccountID) && alertTypes.Contains(a.AlertTypeID))
.OrderBy(a => a.CreateDate.Month)
.GroupBy(a => a.CreateDate.Month)
.ToDictionary(g => firstKeys
.First(k => k > g.OrderByDescending(a => a.CreateDate)
.FirstOrDefault().CreateDate));
var combined = first
.GroupJoin(
second,
fk => fk.Key,
sk => sk.Key,
(d, l) => d.Value
.Union(l.SelectMany(i => i.Value).ToArray()))
.SelectMany(i => i);
result = combined.ToList();
}
return result;
}
Thanks to John, I am much further along. At the moment, I am getting the following error:
Sequence contains no matching element
at this line (I am pretty sure):
.First(k => k > g.OrderByDescending(a => a.CreateDate)
For what it is worth, here is what the data in my Alerts table looks like.
AlertID AccountID CreateDate Timestamp AlertTypeID Message
122 5 2008-03-11 20:48:07.983 0x00000000000128FB 9 sdfs
123 1 2008-03-11 20:48:39.957 0x00000000000128FE 8 sdfsd
124 5 2008-03-11 20:48:39.977 0x00000000000128FF 8 sdfs
125 5 2008-03-11 20:48:40.017 0x0000000000012901 8 asdfa
126 1 2008-03-12 22:57:42.160 0x00000000000130B3 4 sfsf
127 5 2008-03-12 22:57:42.337 0x00000000000130B4 4 sdfsd
128 5 2008-03-13 09:42:14.237 0x0000000000013889 4 sdfsd
129 5 2008-03-13 09:42:31.957 0x000000000001388B 4 sdfsd
130 5 2008-03-13 09:42:45.397 0x000000000001388D 5 asdfsdf
131 1 2008-03-16 14:52:17.197 0x0000000000014822 9 asdfsdf
132 1 2008-04-12 15:25:17.330 0x000000000001B582 3 sfasdf
133 5 2008-04-12 15:25:17.700 0x000000000001B583 3 dfsfds
134 6 2008-04-14 08:37:03.273 0x000000000001BD87 3 aasfsd
135 6 2008-04-14 08:37:15.270 0x000000000001BD89 3 fhfsdf
136 6 2008-04-14 08:38:45.120 0x000000000001BD8B 2 ghsdgd
137 6 2008-04-14 08:41:30.407 0x000000000001BD9A 4 fghsdfg
138 6 2008-04-14 08:42:30.800 0x000000000001BD9C 4 gfsdf
139 6 2008-04-14 08:42:43.763 0x000000000001BD9E 5 sdfsdf
140 6 2008-04-14 08:49:25.450 0x000000000001BDAA 9 sdfasdfa
141 6 2008-04-14 08:49:34.237 0x000000000001BDAC 9 sdfasdf
142 1 2008-04-14 08:50:23.380 0x000000000001BDAF 8 sdfhdfhsg
143 6 2008-04-14 08:50:23.567 0x000000000001BDB0 8 dgasdf
144 5 2008-04-14 08:50:23.690 0x000000000001BDB1 8 dgasdf
145 6 2008-04-14 08:50:23.747 0x000000000001BDB2 8 dgasdf
147 1 2008-06-24 14:22:41.183 0x00000000000222E6 14 dgasdf
148 5 2008-06-24 14:22:41.617 0x00000000000222E7 14 dgasdf
149 6 2008-06-24 14:22:41.623 0x00000000000222E8 14 dgasdf
150 1 2008-06-24 20:11:57.757 0x0000000000022AB3 13 dgasdf
151 5 2008-06-24 20:11:57.947 0x0000000000022AB4 13 dgasdf
152 6 2008-06-24 20:11:57.953 0x0000000000022AB5 13 dgasdf
153 1 2008-07-03 18:41:51.067 0x0000000000028888 14 dgasdf
154 5 2008-07-03 18:41:51.230 0x0000000000028889 14 dgasdf
155 6 2008-07-03 18:41:51.237 0x000000000002888A 14 dgasdf
156 1 2008-07-03 18:46:17.873 0x000000000002888D 14 dgasdf
157 5 2008-07-03 18:46:17.937 0x000000000002888E 14 dgasdf
158 6 2008-07-03 18:46:17.940 0x000000000002888F 14 dgasdf
The key is breaking the two groups down into dictionaries, using the dates from the first list as the keys to the dictionary, and picking the closest key after the second list item date as the key for the second dictionary.
Once you have the two dictionaries, each using a common key value for the types and the subtypes you can just do a GroupJoin and SelectMany to get the results in a sorted list.
(* Note that the answer is based on a slightly different, earlier version of the question, I'm not going to take the time to update the answer because I think the fundamental problem is illustrated and solved in this answer already)
update 2
I realize the problem you're seeing with the First() call is that some of your subalert items might be newer than any other alert item which would cause your exception. I addressed that by adding a 'surrogate' key to the first dictionary using DateTime::MaxValue, and then I no longer filter out the subalerts from the first list, I just use .Distinct() on the final result to remove duplicates
Using linqpad I mocked up this question and solved it using dictionaries and GroupJoin
var all = new []{
new {date = DateTime.Parse("2012-12-23"), type = "alert", value = 1, accountId = 333 },
new {date = DateTime.Parse("2012-12-21"), type = "alert", value = 2, accountId = 333 },
new {date = DateTime.Parse("2012-12-20"), type = "alert", value = 3, accountId = 333 },
new {date = DateTime.Parse("2012-12-18"), type = "alert", value = 4, accountId = 333 },
new {date = DateTime.Parse("2012-12-12"), type = "alert", value = 5, accountId = 333 },
new {date = DateTime.Parse("2012-11-22"), type = "alert", value = 1, accountId = 333 },
new {date = DateTime.Parse("2012-11-16"), type = "alert", value = 2, accountId = 333 },
new {date = DateTime.Parse("2012-11-12"), type = "alert", value = 3, accountId = 333 },
new {date = DateTime.Parse("2012-12-19"), type = "subalert", value = 1, accountId = 333 },
new {date = DateTime.Parse("2012-12-18"), type = "subalert", value = 2, accountId = 333 },
new {date = DateTime.Parse("2012-12-04"), type = "subalert", value = 3, accountId = 333 },
new {date = DateTime.Parse("2012-12-01"), type = "subalert", value = 4, accountId = 333 },
new {date = DateTime.Parse("2012-11-14"), type = "subalert", value = 1, accountId = 333 },
new {date = DateTime.Parse("2012-11-08"), type = "subalert", value = 2, accountId = 333 },
/*add*/ new {date = DateTime.Parse("2012-12-25"), type = "subalert", value = 9, accountId = 333 },
};
var first = all
.Where(a=>a.accountId == 333 /* removed && type != "alert" */)
.OrderByDescending(a=>a.date)
.GroupBy(a=>a.date.Date)
.ToDictionary(g=>g.Key);
var firstKeys = first.Keys
.Cast<DateTime>()
.Union(new []{DateTime.MaxValue}) /* added this 'surrogate' key */
.OrderBy(k=>k)
.ToArray();
var second = all
.Where(a=>a.accountId == 333 && a.type == "subalert")
.OrderBy(a=>a.date.Month)
.GroupBy(a=>a.date.Month)
.ToDictionary(g=>firstKeys.First(k=>k > g.OrderByDescending(a=>a.date).FirstOrDefault().date));
var combined = first
.GroupJoin(
second,
fk=>fk.Key,
sk=>sk.Key,
(d,l)=>d.Value
.Union(l.SelectMany(i=>i.Value).ToArray()))
.SelectMany(i=>i)
.Distinct(); /* Added this to remove duplicates */
combined.Dump();
Which yields:

Categories

Resources