Group data having unique keys and values - c#

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);
}
}
}

Related

Take a group of numbers put them in 3 new groups evenly

To explain in more detail. I need to take a bunch of numbers and place them in classes/groups. Lets say I have 100 numbers. I need to divide that by the number of classes (n) where n = 3 and place them in three groups with 33, 33, 34 numbers respectively. or if (n) = 4 then it would be 4 classes of 25, 25, 25, 25. They also need to stay grouped from highest to lowest.
I have searched and saw a few things relating to LINQ to do this but I haven't wrapped my head around it.
I figured I could put all the numbers in a list, then find the total number in the index divide it by the number of classes to find out how many need to go into each class. My problem comes in is how to pull the numbers out of the list and place them in there respective groups while maintaining there grouping highest to lowest. Result desired for 3 classes with 15 numbers.
List<int> test = new List<int> { 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86 };
int total_indexes = test.Count + 1;
float classes = (total_indexes / 3);
Classes would equal 5 so it would look like this below
Class A:
100
99
98
97
96
Class B:
95
94
93
92
91
Class C:
90
89
88
87
86
Ok so I wrote this piece of code to give you the result you want:
public List<int[]> Do(int[] numbers, int groupCount)
{
numbers = numbers.OrderByDescending(x => x).ToArray();
var result = new List<int[]>();
var itemsCountInEachGroup = numbers.Length / groupCount;
var remainingCount = numbers.Length % groupCount;
var iterateCount = groupCount;
for (int i = 0; i < iterateCount; i++)
{
var skip = i * itemsCountInEachGroup;
//Last iterate
if (i == iterateCount - 1)
{
var n = numbers.Skip(skip).Take(itemsCountInEachGroup + remainingCount).ToArray();
result.Add(n);
}
else
{
var n = numbers.Skip(skip).Take(itemsCountInEachGroup).ToArray();
result.Add(n);
}
}
return result;
}
Example =>
var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
var res = Do(numbers, 5);
This solution is probably not very optimized, and is likely to be improved.
int groupNb = 3, elementNb = 100;
//Populating elements with pseudo-random numbers for demonstration
Random r = new Random();
List<int> elements = new List<int>();
for (int i = 0; i < elementNb; i++)
elements.Add(r.Next(0, 100));
//The groups
List<int>[] groups = new List<int>[groupNb];
//Classifying elements in groups
int currentGroup = 0;
foreach (int value in elements.OrderByDescending(x => x))
{
if (groups[currentGroup] == null)
groups[currentGroup] = new List<int>();
groups[currentGroup].Add(value);
currentGroup = ++currentGroup % groupNb;
}
I did the answer with a PowerShell
open PowerShell ISE
and write the following script:
$Numbers = 100,94,91,90,89,85,84,81,79,74,74,70,95,92,83
$SortedNumbers = $Numbers | sort -Descending
$NumberofClasses = 3
$Countofnumbersinclase = $Numbers.Count / $NumberofClasses
$x = 0
$y = 0
For ($i = 0 ; $i -lt $NumberofClasses ;$i++){
$y = $i+$Countofnumbersinclase-1+$y
$Clasno = $i+1
Write-host "class No $Clasno is " $SortedNumbers[$X..$Y]
$x = $y+1
}
The result as following:
class No 1 is 100 95 94 92 91
class No 2 is 90 89 85 84 83
class No 3 is 81 79 74 74 70
I think exactly s you want and you can add any numbers or any no of classes and it will works
If all groups (with the only exception of the last one) should have equal number of items you can try Linq OrderBy followed by GroupBy:
Code:
using System.Linq;
...
private static List<T>[] Classify<T>(List<T> source, int count)
where T : IComparable<T> {
int size = source.Count / count;
return source
.OrderBy(item => item)
.Select((item, index) => new { item, index })
.GroupBy(pair => Math.Clamp(pair.index / size, 0, count - 1),
pair => pair.item)
.Select(group => group.ToList())
.ToArray();
}
If your C# version doesn't have Math.Clamp you can implement it as
private static int Clamp(int value, int min, int max) {
return value < min ? min :
value > max ? max :
value;
}
Demo:
// Let's split "count" items into "classes" classes
int count = 10;
int classes = 4;
List<int> demo = Enumerable
.Range(1, count)
.ToList();
var result = Classify(demo, classes);
string report = string.Join(Environment.NewLine, result
.Select(list => $"{list.First()} - {list.Last()} ({list.Count} items) : {string.Join(", ", list)}"));
Console.Write(report);
Outcome:
1 - 2 (2 items) : 1, 2
3 - 4 (2 items) : 3, 4
5 - 6 (2 items) : 5, 6
7 - 10 (4 items) : 7, 8, 9, 10

Find out if a list of strings contains permutations of words from another string (counter for each combination)

I didn't know exactly how to ask this question better so I will try to explain it as best as I can.
Let's say I have one list of 20 strings myList1<string> and I have another string string ToCompare. Now each of the strings in the list as well as the string ToCompare have 8 words divided by empty spaces. I want to know how many times combination of any three words from string ToCompare in any possible order is to be found in the strings of myList1<string>. For an example:
This is the list (short version - example):
string1 = "AA BB CC DD EE FF GG HH";
string2 = "BB DD EE AA HH II JJ MM";
.......
string20 = "NN OO AA RR EE BB FF KK";
string ToCompare = "BB GG AA FF CC MM RR II";
Now I want to know how many times any combination of 3 words from ToCompare string is to be found in myList1<string>. To clarify futher three words from ToCompare "BB AA CC" are found in string1 of the list thus the counter for these 3 words would be 1. Another 3 words from ToCompare "BB AA II" are found in the string2 of myList1<string> but the counter here would be also 1 because it's not the same combination of words (I have "AA" and "BB" but also "II". They are not equal). Order of these 3 words doesn't matter, that means "AA BB CC" = "BB AA CC" = "CC BB AA". I want to know how many combinations of all (any) 3 words from ToCompare are found in myList1<string>. I hope it's clear what I mean.
Any help would be appreciated, I don't have a clue how to solve this. Thanks.
Example from Vanest:
List<string> source = new List<string>();
source.Add("2 4 6 8 10 12 14 99");
source.Add("16 18 20 22 24 26 28 102");
source.Add("33 6 97 38 50 34 87 88");
string ToCompare = "2 4 6 15 20 22 28 44";
The rest of the code is exacty the same, and the result:
Key = 2 4 6, Value = 2
Key = 2 4 20, Value = 1
Key = 2 4 22, Value = 1
Key = 2 4 28, Value = 1
Key = 2 6 20, Value = 1
Key = 2 6 22, Value = 1
Key = 2 6 28, Value = 1
Key = 2 20 22, Value = 1
Key = 2 20 28, Value = 1
Key = 2 22 28, Value = 1
Key = 4 6 20, Value = 1
Key = 4 6 22, Value = 1
Key = 4 6 28, Value = 1
Key = 4 20 22, Value = 1
Key = 4 20 28, Value = 1
Key = 4 22 28, Value = 1
Key = 6 20 22, Value = 1
Key = 6 20 28, Value = 1
Key = 6 22 28, Value = 1
Key = 20 22 28, Value = 1
As you can see there are combinations which not exist in the strings, and the value of the first combination is 2 but it comes only one time in the first string
I think this should suffice your ask,
List<string> source = new List<string>();
source.Add("AA BB CC DD EE FF GG HH");
source.Add("BB DD EE AA HH II JJ MM");
source.Add("NN OO AA RR EE BB FF KK");
string ToCompare = "BB GG AA FF CC MM RR II";
string word1, word2, word3, existingKey;
string[] compareList = ToCompare.Split(new string[] { " " }, StringSplitOptions.None);
Dictionary<string, int> ResultDictionary = new Dictionary<string, int>();
for (int i = 0; i < compareList.Length - 2; i++)
{
word1 = compareList[i];
for (int j = i + 1; j < compareList.Length - 1; j++)
{
word2 = compareList[j];
for (int z = j + 1; z < compareList.Length; z++)
{
word3 = compareList[z];
source.ForEach(x =>
{
if (x.Contains(word1) && x.Contains(word2) && x.Contains(word3))
{
existingKey = ResultDictionary.Keys.FirstOrDefault(y => y.Contains(word1) && y.Contains(word2) && y.Contains(word3));
if (string.IsNullOrEmpty(existingKey))
{
ResultDictionary.Add(word1 + " " + word2 + " " + word3, 1);
}
else
{
ResultDictionary[existingKey]++;
}
}
});
}
}
}
ResultDictionary will have the 3 word combinations that occur in myList1<string> with their count of occurrences. To get the total count, retrieve and add all the value fields from ResultDictionary.
EDIT:
Below snippet produces correct result with the given input,
List<string> source = new List<string>();
source.Add("2 4 6 8 10 12 14 99");
source.Add("16 18 20 22 24 26 28 102");
source.Add("33 6 97 38 50 34 87 88");
string ToCompare = "2 4 6 15 20 22 28 44";
string word1, word2, word3, existingKey;
string[] compareList = ToCompare.Split(new string[] { " " }, StringSplitOptions.None);
string[] sourceList, keywordList;
Dictionary<string, int> ResultDictionary = new Dictionary<string, int>();
source.ForEach(x =>
{
sourceList = x.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < compareList.Length - 2; i++)
{
word1 = compareList[i];
for (int j = i + 1; j < compareList.Length - 1; j++)
{
word2 = compareList[j];
for (int z = j + 1; z < compareList.Length; z++)
{
word3 = compareList[z];
if (sourceList.Contains(word1) && sourceList.Contains(word2) && sourceList.Contains(word3))
{
existingKey = ResultDictionary.Keys.FirstOrDefault(y =>
{
keywordList = y.Split(new string[] { " " }, StringSplitOptions.None);
return keywordList.Contains(word1) && keywordList.Contains(word2) && keywordList.Contains(word3);
});
if (string.IsNullOrEmpty(existingKey))
{
ResultDictionary.Add(word1 + " " + word2 + " " + word3, 1);
}
else
{
ResultDictionary[existingKey]++;
}
}
}
}
}
});
Hope this helps...
I think this will do what you're asking for:
void Main()
{
var list =
new List<String>
{
"AA BB CC DD EE FF GG HH",
"BB DD EE AA HH II JJ MM",
"NN OO AA RR EE BB FF KK"
};
var toCompare = "BB GG AA FF CC MM RR II";
var permutations = CountPermutations(list, toCompare);
}
public Int32 CountPermutations(List<String> list, String compare)
{
var words = compare.Split(' ');
return list
.Select(l => l.Split(' '))
.Select(l => new { String = String.Join(" ", l), Count = l.Join(words, li => li, wi => wi, (li, wi) => li).Count()})
.Sum(x => x.Count - 3);
}
[edit: 2/20/2019]
You can use the following to get all the matches to each list item with the total number of unique combinations
void Main()
{
var list =
new List<String>
{
"AA BB CC DD EE FF GG HH",
"BB DD EE AA HH II JJ MM",
"NN OO AA RR EE BB FF KK",
"AA AA CC DD EE FF GG HH"
};
list.Select((l, i) => new { Index = i, Item = l }).ToList().ForEach(x => Console.WriteLine($"List Item{x.Index + 1}: {x.Item}"));
var toCompare = "BB GG AA FF CC MM RR II";
Console.WriteLine($"To Compare: {toCompare}");
Func<Int32, Int32> Factorial = x => x < 0 ? -1 : x == 0 || x == 1 ? 1 : Enumerable.Range(1, x).Aggregate((c, v) => c * v);
var words = toCompare.Split(' ');
var matches = list
// Get a list of the list items with all their parts
.Select(l => new { Parts = l.Split(' '), Original = l })
// Join each part from the to-compare item to each part of the list item
.Select(l => new { String = String.Join(" ", l), Matches = l.Parts.Join(words, li => li, wi => wi, (li, wi) => li), l.Original })
// Only consider items with at least 3 matches
.Where(l => l.Matches.Count() >= 3)
// Get the each item including how many parts matched and how many unique parts there are of each part
.Select(l => new { l.Original, Matches = String.Join(" ", l.Matches), Count = l.Matches.Count(), Groups = l.Matches.GroupBy(m => m).Select(m => m.Count()) })
// To calculate the unique combinations for each match use the following mathematical equation: match_count! / (frequency_part_1! * frequency_part_2! * ... * frequency_part_n!)
.Select(l => new { l.Original, l.Matches, Combinations = Factorial(l.Count) / l.Groups.Aggregate((c, v) => c * Factorial(v)) })
.ToList();
matches.ForEach(m => Console.WriteLine($"Original: {m.Original}, Matches: {m.Matches}, Combinations: {m.Combinations}"));
var totalUniqueCombinations = matches.Sum(x => x.Combinations);
Console.WriteLine($"Total Unique Combinations: {totalUniqueCombinations}");
}

C# solution for number of ways to choose 2 numbers from different groups of numbers

The problem I'm trying to solve is like this.
I'm given N, telling me the range of numbers is 0, 1, ..., N-2, N-1.
I'm also given pairings, telling me that those number pairs are in the same "group".
Ex:
N=6, 0 and 1 are paired, 1 and 4 are paired, and 2 and 3 are paired, and I know that the remaining numbers are in their own groups.
Then I know the groupings are {0, 1, 4}, {2, 3}, {5}.
The number of ways to choose two numbers from different groups among these groups is 11 and that's the number I'm trying to solve for. These choices are:
{0,2},
{0,3},
{0,5},
{1,2},
{1,3},
{1,5},
{4,2},
{4,3},
{4,5},
{2,5},
{3,5}
Can someone help figure out where my logic is wrong? Because I'm failing larger test cases and passing smaller ones.
My code:
static int Combinations(int N, int K)
{
decimal result = 1;
for (int i = 1; i <= K; i++)
{
result *= N - (K - i);
result /= i;
}
return (int)result;
}
/// <summary>
/// Given a set of n numbers 0, 1, ..., n-1 and a list of pairs
/// of the numbers from the set where each pair (i,j) means that
/// number i and j are in the same group, find the number of ways
/// to choose 2 numbers that are not in the same group.
/// </summary>
static int PoliticallyCorrectPairs(int n, Tuple<int, int>[] pairs)
{
// Create a map that from each number to a number representing
// the group that it is in. For example, if n = 5 and
// pairs = { (0, 1), (2, 3), (0, 4) }, then the map will look
// like
// 0 -> 1
// 1 -> 1
// 2 -> 2
// 3 -> 2
// 4 -> 1
// indicating that 0,1,4 belong to one group and 2,3 to the other.
int[] gmap = new int[n];
int k = 1;
foreach(Tuple<int, int> pair in pairs)
{
int i = pair.Item1,
j = pair.Item2;
// If both i and j are already in groups, combine those into a
// single group if it isn't already.
if (gmap[i] != 0 && gmap[j] != 0)
{
if(gmap[i] != gmap[j])
{
for(int m = 0; m < n; ++m)
if(gmap[m] == gmap[j])
gmap[m] = gmap[i];
}
}
else if(gmap[j] != 0) // j is in a group and i isn't
gmap[i] = gmap[j]; // add i to j's group
else if(gmap[i] != 0) // i is in a group and j isn't
gmap[j] = gmap[i]; // add j to i's group
else
gmap[i] = gmap[j] = k++; // Put both in same new group.
}
// Those not assigned to a group each end up in a group alone.
for(int i = 0; i < gmap.Length; ++i)
if(gmap[i] == 0)
gmap[i] = k++;
// Get the group sizes as an array.
int[] gcnts = gmap.GroupBy(x => x).Select(g => g.Count()).ToArray();
// Total number of ways to choose pairs of numbers.
int totalCombinations = Combinations(n, 2);
// Number of pairs from previous count that are in the same group.
int impossibleCombinations = gmap.GroupBy(x => x)
.Select(g => g.Count())
.Where(count => count > 1)
.Sum(count => Combinations(count, 2));
return totalCombinations - impossibleCombinations;
}
For example, I am passing
[TestMethod]
public void Sample1()
{
int N = 5;
Tuple<int, int>[] pairs = new Tuple<int, int>[]
{
Tuple.Create(0, 1),
Tuple.Create(2, 3),
Tuple.Create(0, 4)
};
Assert.AreEqual(6, PoliticallyCorrectPairs(N, pairs));
}
but failing
[TestMethod]
public void TestCase2()
{
int N = 100;
Tuple<int, int>[] pairs =
#"0 11
2 4
2 95
3 48
4 85
4 95
5 67
5 83
5 42
6 76
9 31
9 22
9 55
10 61
10 38
11 96
11 41
12 60
12 69
14 80
14 99
14 46
15 42
15 75
16 87
16 71
18 99
18 44
19 26
19 59
19 60
20 89
21 69
22 96
22 60
23 88
24 73
27 29
30 32
31 62
32 71
33 43
33 47
35 51
35 75
37 89
37 95
38 83
39 53
41 84
42 76
44 85
45 47
46 65
47 49
47 94
50 55
51 99
53 99
56 78
66 99
71 78
73 98
76 88
78 97
80 90
83 95
85 92
88 99
88 94"
.Split('\n')
.Select(line =>
{
int[] twofer = line.Split(' ').Select(s => int.Parse(s)).ToArray();
return Tuple.Create(twofer[0], twofer[1]);
})
.ToArray();
Assert.AreEqual(3984, PoliticallyCorrectPairs(N, pairs));
}
Any idea where I'm going wrong?
The problem is in the combining part:
if(gmap[i] != gmap[j])
{
for(int m = 0; m < n; ++m)
if(gmap[m] == gmap[j]) // here
gmap[m] = gmap[i];
}
When m hits j, the gmap[j] is replaced with gmap[i], hence the rest of the elements are checked against gmap[i].
Simply put the replaced value into local variable:
if(gmap[i] != gmap[j])
{
int g = gmap[j];
for(int m = 0; m < n; ++m)
if(gmap[m] == g)
gmap[m] = gmap[i];
}

LINQ to get two integers in an array whose sum is closest to zero

Given an array of integers (with +ve and -ve numbers which are unordered),
what is the LINQ statement to find those two numbers whose sum is closest to 0
E.g int[] a = new int[]{2, 56, -11, 15, 12, 10, 43, -59, -13}
In the above set of integers {-11,10} and {12,-13} are the two set of two integers which is closest to 0
I could not get much, except the following basic query of LINQ as i was not sure how to proceed,
var res = from x in a
WHERE //i am stuck in the logic what to write here
select x
If it can be any two values in the set then you can do this...
var set = new int[] { 2, 56, -11, 15, 12, 10, 43, -59, -13 };
var ret = (from a in set
from b in set
orderby Math.Abs(a + b)
select new
{
a,
b
}).First();
Console.WriteLine(ret); // {a: -11, b:12 }
If you want slightly better performance for large sets (and can assume there are negative and positive values mixed) you can do this.
var set = new int[] { 2, 56, -11, 15, 12, 10, 43, -59, -13 };
var ret = (from a in set.Where(x => x >= 0)
from b in set.Where(y => y < 0)
orderby Math.Abs(a + b)
select new
{
a,
b
}).First();
Console.WriteLine(ret); // { a= 12, b= -11}
And since you now want the entire set of matches excluding matching to one's self...
var set = new int[] { 2, 56, -11, 15, 12, 10, 43, -59, -13 };
var ret = from a in set
from b in set
where a != b
let c = new { a, b }
group c by Math.Abs(c.a + c.b);
var minset = ret.First(i => i.Key == ret.Min(j => j.Key))
.Select(s=>s);
Console.WriteLine(minset.Aggregate(new StringBuilder(),
(sb,v)=>sb.Append(v)
.AppendLine()
));
/*
{ a = -11, b = 12 }
{ a = -11, b = 10 }
{ a = 12, b = -11 }
{ a = 12, b = -13 }
{ a = 10, b = -11 }
{ a = -13, b = 12 }
*/
And to dedup....
var set = new[] { 2, 56, -11, 15, 12, 10, 43, -59, -13 };
var ret = from a in set
from b in set
where a != b
let c = new { a, b }
group c by Math.Abs(c.a + c.b);
var minset = ret.First(i => i.Key == ret.Min(j => j.Key))
.Select(s => new { a = Math.Min(s.a, s.b), b = Math.Max(s.a, s.b) })
.Distinct();
Console.WriteLine(minset.Aggregate(new StringBuilder(),
(sb, v) => sb.Append(v)
.AppendLine()));
/*
{ a = -11, b = 12 }
{ a = -11, b = 10 }
{ a = -13, b = 12 }
*/
var data = new [] { 2, 56, -11, 15, 12, 10, 43, -59, -13 };
// TODO : assert data.Length >= 2
var query = from i in Enumerable.Range (0, data.Length - 2)
from j in Enumerable.Range (i + 1, data.Length - 1 - i)
let x = data[i] let y = data[j]
group new { x, y } by Math.Abs (x + y) into g
orderby g.Key select g;
Console.WriteLine (string.Join ("\n", query.First ()));
{ x = -11, y = 12 }
{ x = -11, y = 10 }
{ x = 12, y = -13 }

LINQ merge 2 query results

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

Categories

Resources