LINQ merge 2 query results - c#

The datatable has 5 columns
Name Class Course Month Score
Alex C1 Math 12 90
Bob C1 Chem 11 91
Alex C2 Math 11 91
Alex C1 Math 11 89
Bob C1 Chem 12 97
Alex C1 Math 10 94
Alex C2 Chem 12 92
Bob C2 Math 12 94
And I wanna group (name, class) and fetch the max math score in just Nov and Dec, and the max chem score. Heres my query code
DataRow[] dr1 = dt.Select("Course = 'Math' AND Month > 10");
var result_one = dr1.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Max = g.Max(r => r.Field<int>("Score")),
Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
DataRow[] dr2 = dt.Select("Course = 'Chem'");
var result_two = dr2.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Max = g.Max(r => r.Field<int>("Score")),
Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
And I could output these 2 query results as this:
Name Class Math_Max_Month Math_Max
Alex C1 12 90
Alex C2 11 91
Bob C2 12 94
Name Class Chem_Max_Month Chem_Max
Bob C1 12 97
Alex C2 12 92
But how can I merge these 2 results into 1 output such as this:
Name Class Math_Max_Month Math_Max Chem_Max_Month Chem_Max
Alex C1 12 90 null null
Alex C2 11 91 12 92
Bob C1 null null 12 97
Bob C2 12 94 null null
I've tried to use result_one.Concat(result_two) and result_one.Union(result_two), but both are incorrect.

Alright, seems a bit complicated in your example. So i'll give you an answer on a int[] instead of DataRow[]
int[] first = new int[] { 3, 5, 6, 9, 12, 14, 18, 20, 25, 28 };
int[] second = new int[] { 30, 32, 34, 36, 38, 40, 42, 44, 46, 48 };
int[] result = first
.Concat(second)
.OrderBy(x => x)
.ToArray();
Output will be
// 3, 5, 6, 9, 12, 14, 18, 20, 25, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48
Console.Write(String.Join(", ", result));
theoretically this should work in your case, sense we're only dealing with arrays.

This works perfectly well for your code.,
DataRow[] dr1 = dtt.Select("Course = 'Math' AND Month > 10");
var result_one = dr1.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Max = g.Max(r => r.Field<int>("Score")),
Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
DataRow[] dr2 = dtt.Select("Course = 'Chem'");
var result_two = dr2.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Chem_Max = g.Max(r => r.Field<int>("Score")),
Chem_Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
Left Join...
var lstLeftJoin = (from a in result_one
join b in result_two
on new { a.Name, a.Class } equals new { b.Name, b.Class }
into gj
from subpet in gj.DefaultIfEmpty()
select new { a.Name, a.Class, Math_Max_Month = a.Max_Month, Math_Max = a.Max, Chem_Max_Month = (subpet == null ? 0 : subpet.Chem_Max_Month), Chem_Max = (subpet == null ? 0 : subpet.Chem_Max) }).ToList();
Right Join...
var lstRightJoin = (from a in result_two
join b in result_one
on new { a.Name, a.Class } equals new { b.Name, b.Class }
into gj
from subpet in gj.DefaultIfEmpty()
select new { a.Name, a.Class, Math_Max_Month = (subpet == null ? 0 : subpet.Max_Month), Math_Max = (subpet == null ? 0 : subpet.Max), a.Chem_Max_Month, a.Chem_Max }).ToList();
Finaly the Union...
var lstUnion = lstLeftJoin.Select(s => new { Name = s.Name, Class = s.Class, Math_Max_Month = s.Math_Max_Month, Math_Max = s.Math_Max, Chem_Max_Month = s.Chem_Max_Month, Chem_Max = s.Chem_Max }).Union(lstRightJoin.Select(s => new { Name = s.Name, Class = s.Class, Math_Max_Month = s.Math_Max_Month, Math_Max = s.Math_Max, Chem_Max_Month = s.Chem_Max_Month, Chem_Max = s.Chem_Max })).OrderBy(o => o.Name).ThenBy(c => c.Class).ToList();
RESULT
Name Class Math_Max_Month Math_Max Chem_Max_Month Chem_Max
Alex C1 12 90 null null
Alex C2 11 91 12 92
Bob C1 null null 12 97
Bob C2 12 94 null null

Related

Group data having unique keys and values

I have a following set of numbers:
1 137
1 143
11 37
11 46
11 132
46 65
46 139
69 90
Now, I need to group the data by the first value in a way that no group key is present in the group values. So, for instance, if I were to simply group data, I'd get this result:
1 137
143
11 37
46
132
46 65
139
69 90
46 here is a group key in the third group and a group value in the second group. In this case I need to merge the group values of the third group into a second group and remove the third group.
The end result of the grouping should look like this:
1 137
143
11 37
46
132
65
139
69 90
I'm relatively new to C#, so I was wondering if there's a fancy way to do it using LINQ.
Try this LINQ solution:
var numbers = new List<Number>
{
new Number {X = 1, Y = 137},
new Number {X = 1, Y = 143},
new Number {X = 11, Y = 37},
new Number {X = 11, Y = 46},
new Number {X = 11, Y = 132},
new Number {X = 46, Y = 65},
new Number {X = 46, Y = 139},
new Number {X = 69, Y = 90}
};
var result = numbers.GroupBy(c => c.X);
var result2 = numbers.FirstOrDefault(c => result.Select(d => d.Key).Contains(c.Y));
var finalResult = numbers.Where(x => x.X == result2?.Y)
.Select(x => { x.X = result2.X;x.Y = x.Y; return x; } )
.Union(numbers.Where(c => c.X != result2?.Y)).GroupBy(c => c.X ,
(key, element) => new
{
Key = key,
Element = element.Select(c => c.Y).ToList()
});
The result:
This could work for you
public static List<Numbers> setNumbers()
{
List<Numbers> num = new List<Numbers>();
num.Add(new Numbers() { Column1 = 1, Column2 = 137 });
num.Add(new Numbers() { Column1 = 1, Column2 = 143 });
num.Add(new Numbers() { Column1 = 11, Column2 = 37 });
num.Add(new Numbers() { Column1 = 11, Column2 = 46 });
num.Add(new Numbers() { Column1 = 11, Column2 = 132 });
num.Add(new Numbers() { Column1 = 46, Column2 = 65 });
num.Add(new Numbers() { Column1 = 46, Column2 = 139 });
num.Add(new Numbers() { Column1 = 69, Column2 = 90 });
return num;
}
public static void group()
{
List<Numbers> numbers = setNumbers();
var grouppedNumbers = numbers
.GroupBy(x => x.Column1).ToList();
grouppedNumbers.AddRange(grouppedNumbers.FirstOrDefault(x => x.First().Column1.Equals(46)).Select(s => new Numbers() { Column1 = 11, Column2 = s.Column2 }).GroupBy(g => g.Column1).ToList());
grouppedNumbers.Remove(grouppedNumbers.FirstOrDefault(x => x.First().Column1.Equals(46)));
foreach (var groups in grouppedNumbers)
{
Console.WriteLine(groups.First().Column1);
foreach(var i in groups)
{
Console.WriteLine(i.Column1+" "+ i.Column2);
}
}
}

SQL not in - LINQ

SQL
select * from tempFinal where TypeId not in (13,14,15,51,52,55,59) -- Note: From Stored
Current LINQ
int LeaveExists = (from dt in db.LeaveDetails join
leavH in db.LeaveHeaders on dt.LeaveHeaderId equals leavH.LeaveHeaderId
where (leavH.LeaveTypeId != 55 && leavH.LeaveTypeId != 59 && . . . . )
&& dt.TestId == Id
select dt).Count();
I saw this LINK and test the code.
int[] tempNotIn = new int[] { 13, 14, 15, 51, 52, 55, 59 };
int LeaveExists = (from dt in db.LeaveDetails join
leavH in db.LeaveHeaders on dt.LeaveHeaderId equals leavH.LeaveHeaderId
where !tempNotIn.Contains((int)leavH.LeaveTypeId) &&
dt.TestId == Id
select dt).Count();
it gives me an error The name LeavH does not exist in the current context.
TestId is INT
To all who view my post I got it. After a while of Trial and came up to this query and it's working
int[] tempNotIn = new int[] { 13, 14, 15, 51, 52, 55, 59 };
var LeaveExists = (from dt in db.LeaveDetails join
leavH in db.LeaveHeaders on dt.LeaveHeaderId equals leavH.LeaveHeaderId
where !tempNotIn.Contains((int)leavH.LeaveTypeId) &&
dt.TestId == Id
select dt).Count();

Grouping, dynamic key formation, pivitoing information in C# & generating json output [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I am getting some hierarchical data in flat format from SQL server. This is how the data looks.
Group Name Level Parent Value1 Value2 RowID
Global Product1 1 10 20 1
APAC Product1 1 80 90 2
EMEA Product1 1 50 70 3
Global Product2 2 Product1 100 200 4
APAC Product2 2 Product1 800 900 5
EMEA Product2 2 Product1 500 700 6
Global Product3 3 Product2 10 20 7
APAC Product3 3 Product2 80 90 8
Global Product4 4 Product3 110 120 9
APAC Product4 4 Product3 810 190 10
EMEA Product4 4 Product3 510 170 11
.....
.....
Global Product12 1 10 20 100
I need to group the data based on Name and Level columns. Then I need to create multiple keys based on combination of Group Column & Value1 & Value2 columns. See the below JSON output to get more idea about the output. Please keep in mind number of Group can change.
[
{
Id: 1,
Name: Product1,
parentId: null,
GlobalValue1: 10,
GlobalValue2: 20,
APACValue1: 80,
APACValue2: 90,
EMEAValue1: 50,
EMEAValue2: 70
},
{
Id: 2,
Name: Product2,
parentId: 1,
GlobalValue1: 100,
GlobalValue2: 200,
APACValue1: 800,
APACValue2: 900,
EMEAValue1: 500,
EMEAValue2: 700
},
{
Id: 3,
Name: Product3,
parentId: 2,
GlobalValue1: 10,
GlobalValue2: 20,
APACValue1: 80,
APACValue2: 90,
EMEAValue1: null,
EMEAValue2: null
},
{
Id: 4,
Name: Product4,
parentId: 3,
GlobalValue1: 110,
GlobalValue2: 120,
APACValue1: 810,
APACValue2: 190,
EMEAValue1: 510,
EMEAValue2: 170
}{
Id: 122,
Name: Product12,
parentId: null3,
GlobalValue1: 10,
GlobalValue2: 20,
APACValue1: null,
APACValue2: null,
EMEAValue1: null,
EMEAValue2: null
}
]
I need to auto generate ID field. This field will be use to establish parent-child relationship. I cannot rely onnamecolumn as it can repeat (can have same value) across different levels.. There will be more than 1 product and each level. In my example, I am just showing one product per level just to keep example simple.
Here is a bit of code that does what I think you want. It's a console application and I used Newtonsoft.Json (get it with NuGet in references) to create the JSON.
class Program
{
static void Main(string[] args)
{
var data = initData();
var result = getGroupedData(data);
var json = new Newtonsoft.Json.JsonSerializer
{
Formatting=Formatting.Indented
};
json.Serialize(Console.Out,result);
Console.ReadKey();
}
private static object getGroupedData(DataTable dt)
{
var id = 1;
var rows = dt.Rows.OfType<DataRow>(); //because for some reason DataRowCollection is not an IEnumerable<DataRow>
var products = rows //the products, each defined by Name and Level
.GroupBy(r => new { Name = r.Field<string>("Name"), Level = r.Field<int>("Level") })
.Select(g => new
{
Id = id++, //create the id
Name = g.Key.Name,
Level = g.Key.Level,
// select the parent and throw exception if there are more or less than one
Parent = g.Select(r => r.Field<string>("Parent")).Distinct().Single()
}).ToList();
var results = products
.Select(p => new //need a partial result first, containing the Global, Apac and Emea rows, if they exist
{
Id = p.Id,
Name = p.Name,
// Assuming the Level of a child is Level of parent+1
Parent = products.FirstOrDefault(par => par.Name == p.Parent && par.Level + 1 == p.Level),
Global = rows.FirstOrDefault(r => r.Field<string>("Name") == p.Name && r.Field<int>("Level") == p.Level && r.Field<string>("Group") == "Global"),
Apac = rows.FirstOrDefault(r => r.Field<string>("Name") == p.Name && r.Field<int>("Level") == p.Level && r.Field<string>("Group") == "APAC"),
Emea = rows.FirstOrDefault(r => r.Field<string>("Name") == p.Name && r.Field<int>("Level") == p.Level && r.Field<string>("Group") == "EMEA")
})
.Select(x => new //create the final result
{
Id = x.Id,
Name = x.Name,
ParentId = x.Parent==null? (int?)null :x.Parent.Id,
GlobalValue1 = x.Global == null ? (double?)null : x.Global.Field<double?>("Value1"),
GlobalValue2 = x.Global == null ? (double?)null : x.Global.Field<double?>("Value2"),
APACValue1 = x.Apac == null ? (double?)null : x.Apac.Field<double?>("Value1"),
APACValue2 = x.Apac == null ? (double?)null : x.Apac.Field<double?>("Value2"),
EMEAValue1 = x.Emea == null ? (double?)null : x.Emea.Field<double?>("Value1"),
EMEAValue2 = x.Emea == null ? (double?)null : x.Emea.Field<double?>("Value2")
})
.ToArray();
return results;
}
private static DataTable initData()
{
var dt = new DataTable();
dt.Columns.Add("Group", typeof(string));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Level", typeof(int));
dt.Columns.Add("Parent", typeof(string));
dt.Columns.Add("Value1", typeof(double));
dt.Columns.Add("Value2", typeof(double));
dt.Columns.Add("RowID", typeof(int));
dt.Rows.Add("Global", "Product1", 1, null, 10, 20, 1);
dt.Rows.Add("APAC", "Product1", 1, null, 80, 90, 2);
dt.Rows.Add("EMEA", "Product1", 1, null, 50, 70, 3);
dt.Rows.Add("Global", "Product2", 2, "Product1", 100, 200, 1);
dt.Rows.Add("APAC", "Product2", 2, "Product1", 800, 900, 2);
dt.Rows.Add("EMEA", "Product2", 2, "Product1", 500, 700, 3);
dt.Rows.Add("Global", "Product3", 3, "Product2", 10, 20, 1);
dt.Rows.Add("APAC", "Product3", 3, "Product2", 80, 90, 2);
dt.Rows.Add("Global", "Product4", 4, "Product3", 110, 120, 1);
dt.Rows.Add("APAC", "Product4", 4, "Product3", 810, 190, 2);
dt.Rows.Add("EMEA", "Product4", 4, "Product3", 510, 170, 3);
return dt;
}
}
With POCO object, the method would look like this:
private static object getGroupedData(IEnumerable<MyPoco> rows)
{
var id = 1;
var products = rows //the products, each defined by Name and Level
.GroupBy(r => new { Name = r.Name, Level = r.Level })
.Select(g => new
{
Id = id++, //create the id
Name = g.Key.Name,
Level = g.Key.Level,
// select the parent and throw exception if there are more or less than one
Parent = g.Select(r => r.Parent).Distinct().Single()
}).ToList();
var results = products
.Select(p => new //need a partial result first, containing the Global, Apac and Emea rows, if they exist
{
Id = p.Id,
Name = p.Name,
// Assuming the Level of a child is Level of parent+1
Parent = products.FirstOrDefault(par => par.Name == p.Parent && par.Level + 1 == p.Level),
Global = rows.FirstOrDefault(r => r.Name == p.Name && r.Level == p.Level && r.Group == "Global"),
Apac = rows.FirstOrDefault(r => r.Name == p.Name && r.Level == p.Level && r.Group == "APAC"),
Emea = rows.FirstOrDefault(r => r.Name == p.Name && r.Level == p.Level && r.Group == "EMEA")
})
.Select(x => new //create the final result
{
Id = x.Id,
Name = x.Name,
ParentId = x.Parent==null? (int?)null :x.Parent.Id,
GlobalValue1 = x.Global == null ? (double?)null : x.Global.Value1,
GlobalValue2 = x.Global == null ? (double?)null : x.Global.Value2,
APACValue1 = x.Apac == null ? (double?)null : x.Apac.Value1,
APACValue2 = x.Apac == null ? (double?)null : x.Apac.Value2,
EMEAValue1 = x.Emea == null ? (double?)null : x.Emea.Value1,
EMEAValue2 = x.Emea == null ? (double?)null : x.Emea.Value2
})
.ToArray();
return results;
}

Linq for rank() equivalent in SQL Server -

How can order my list using Linq equals rank() over in SQL ?
For example rank is my List<Player>
class Player
{
public int Id;
public int RankNumber;
public int Points;
public int Name;
}
Original Rank list:
RankNumber Points Name Id
1 100 James 01
2 80 Mike 50
3 80 Jess 22
4 50 Jack 11
5 50 Paul 03
6 10 Monik 13
I need this Rank:
RankNumber Points Name Id
1 100 James 01
2 80 Mike 50
2 80 Jess 22
4 50 Jack 11
4 50 Paul 03
6 10 Monik 13
I don't think there is a good way to convert this directly to Linq to SQL but you could do this:
var rankedPlayers = players
.OrderByDescending(p => p.Points)
.Select((p, r) => new Player
{
Id = p.Id,
RankNumber = players.Where(pl => pl.Points > p.Points).Count() + 1,
Points = p.Points,
Name = p.Name
});
It gives you the correct output but will convert horribly and inefficiently to SQL. So I would suggest this modification which materialises the data to a list before creating the ranks:
var rankedPlayers = players
.OrderByDescending(p => p.Points)
.ToList() //<--- Add this
.Select((p, r) => new Player
{
Id = p.Id,
RankNumber = players.Where(pl => pl.Points > p.Points).Count() + 1,
Points = p.Points,
Name = p.Name
});
You can try below expression:
var newData = players
.OrderByDescending(x => x.Points)
.GroupBy(x => x.Points)
.SelectMany((x, index) => x.Select(y => new Player
{
Name = y.Name,
Points = y.Points,
RankNumber = index + 1,
Id = y.Id
}));
players contains IEnumerable of objects of type Player and newData contains ordered data with rank.

Need help Linq Query Group By + Count

I have following Table with records for ActivityLog:
ID, UserId, Category, Created
1 11 DeAssigned 05/10/2012
2 11 LogIn 05/11/2012
3 20 Assigned 06/15/2012
4 11 Assigned 06/10/2012
5 20 DeAssigned 06/13/2012
6 20 Assigned 07/12/2012
7 11 DeAssigned 07/16/2012
8 20 Assigned 08/15/2012
...
now i want to query the Table to create same struct the same Result such as this:
var data = new[] {
new { Month = "05", Assigned = 14, DeAssigned = 5, LogIn=1 },
new { Month = "06", Assigned = 5, DeAssigned = 2, LogIn=0 },
new { Month = "07", Assigned = 50, DeAssigned = 8, LogIn=0 },
new { Month = "08", Assigned = 15, DeAssigned = 1, LogIn=0 }
};
what i have achieved:
var result = (from l in repoA.GetAll()
where l.Created >= System.DateTime.Now.AddYears(-1)
group l by new { l.Created.Value.Month, l.Category }
into groups
orderby groups.Key.Month
select new
{
Month = groups.Key.Month,
Category = groups.Key.Category,
Count = groups.Count()
});
there is not optimal result but count all Activity's grouped by Month:
[0] {Month = 6, Category = Assigned, Count = 2}
[0] {Month = 6, Category = Designed, Count = 1}
[0] {Month = 6, Category = LogIn, Count = 1}
[0] {Month = 7, Category = Assigned, Count = 3}
How can i query my Table to Format my Result in "Horizontal Counted" format?
Or simplier:
var result = (from l in repoA.GetAll()
where l.Created >= System.DateTime.Now.AddYears(-1)
group l by l.Created.Month into groups
orderby groups.Key ascending
select new
{
Month = groups.Key,
Assigned = groups.Where(g => g.Category == "Assigned").Count(),
Deassigned = groups.Where(g => g.Category == "DeAssigned").Count(),
LogIn = groups.Where(g => g.Category == "LogIn").Count()
});

Categories

Resources