Linq join two List<T> using multiples flags - c#
Hello everyone today i got confused trying to join two List using ArticleId and CellarId as Flag, if the both Flags match then subtract and display the result. This is my code:
Articles Class:
public class Articles
{
public Int32 ArticleId { get; set; }
public Int32 CellarId { get; set; }
public Double ArticleQuantity { get; set; }
}
Array data and display results:
List<Articles> arr1 = new List<Articles>();
arr1.Add(new Articles { ArticleId = 1, CellarId = 2, ArticleQuantity = 10.50 });
arr1.Add(new Articles { ArticleId = 2, CellarId = 2, ArticleQuantity = 5.00 });
arr1.Add(new Articles { ArticleId = 5, CellarId = 1, ArticleQuantity = 2.00 });
arr1.Add(new Articles { ArticleId = 1, CellarId = 1, ArticleQuantity = 4.00 });
List<Articles> arr2 = new List<Articles>();
arr2.Add(new Articles { ArticleId = 5, CellarId = 2, ArticleQuantity = 1.00 });
arr2.Add(new Articles { ArticleId = 3, CellarId = 2, ArticleQuantity = 0.50 });
arr2.Add(new Articles { ArticleId = 1, CellarId = 1, ArticleQuantity = 5.00 });
foreach (var a in GetArrayDifferences3D(arr1, arr2))
{
Console.WriteLine("ArticleId: {0}, CellarId: {1}, Qty: {2}",
a.ArticleId, a.CellarId, a.ArticleQuantity);
}
Join using linq function:
public static List<Articles> GetArrayDifferences3D(
List<Articles> Array1,
List<Articles> Array2)
{
List<Articles> ArrayDifferences = new List<Articles>();
ArrayDifferences = Array1.Concat(Array2)
.GroupBy(g => new { g.ArticleId, g.CellarId })
.Select(s => new Articles
{
ArticleId = s.Select(articles => articles.ArticleId).First(),
CellarId = s.Select(cellars => cellars.CellarId).First(),
ArticleQuantity = (s.Select(a => a.ArticleQuantity).Sum())
}).ToList();
return ArrayDifferences;
}
I need to return this result:
ArticleId: 1, CellarId: 2, Qty: -10.50
ArticleId: 2, CellarId: 2, Qty: -5.00
ArticleId: 5, CellarId: 1, Qty: -2.00
ArticleId: 1, CellarId: 1, Qty: 1.00
ArticleId: 5, CellarId: 2, Qty: 1.00
ArticleId: 3, CellarId: 2, Qty: 0.50
My Current result:
ArticleId: 1, CellarId: 2, Qty: 10.5
ArticleId: 2, CellarId: 2, Qty: 5
ArticleId: 5, CellarId: 1, Qty: 2
ArticleId: 1, CellarId: 1, Qty: 9
ArticleId: 5, CellarId: 2, Qty: 1
ArticleId: 3, CellarId: 2, Qty: 0.5
any help is appreciated, Thanks
The way your code is currently written, you would need ArticleQuantity in arr1 to be negative rather than positive for the Sum() to give you the expected result. I suppose that array represents quantity sold, to be subtracted from quantity on hand.
A simple solution would be to create a new version of arr1:
var arr1Negative = arr1.Select(a =>
new Articles()
{
ArticleId = a.ArticleId,
CellarId = a.CellarId,
ArticleQuantity = -a.ArticleQuantity
});
and use that in place of arr1.
You could also move similar logic into GetArrayDifferences3D().
Related
Count rows in C# list with same Group and set as int value on other property [duplicate]
This question already has answers here: C# List grouping and assigning a value (2 answers) Closed 6 years ago. I need help to solve this. I have used Console Application to solve this and I am stuck. Every row in ORDER needs to have an unique value inside a group med same GROUP-value. The Max-value on ORDER inside the group need to be Count of Rows -1 and Min value has to be 0. The sorting doesnt matter. It only needs to be an UNIQUE value between min and max. Example: 012345 or 041532 ID GROUP VALUE ORDER 1 1 10 0 2 1 2 0 3 2 1 0 4 3 2 0 5 3 6 0 6 3 1 0 7 3 9 0 GROUP 1 have 2(-1) Rows, ORDER value has to be 0-1. GROUP 2 have 1(-1) Rows, ORDER value has to be 0. GROUP 3 have 4(-1) Rows, ORDER value has to be 0-3. End Result: ID GROUP VALUE ORDER 1 1 10 0 2 1 2 1 3 2 1 0 4 3 2 0 5 3 6 1 6 3 1 3 7 3 9 2 Here is the properties i have used. public class OrderRow { public int ID { get; set; } public int GROUP { get; set; } public int VALUE { get; set; } public int ORDER { get; set; } } new OrderRow {ID = 1, GROUP = 1, VALUE = 10, ORDER = 0}, new OrderRow {ID = 2, GROUP = 1, VALUE = 2, ORDER = 0}, new OrderRow {ID = 3, GROUP = 2, VALUE = 1, ORDER = 0}, new OrderRow {ID = 4, GROUP = 3, VALUE = 2, ORDER = 0}, new OrderRow {ID = 5, GROUP = 3, VALUE = 6, ORDER = 0}, new OrderRow {ID = 6, GROUP = 3, VALUE = 1, ORDER = 0}, new OrderRow {ID = 7, GROUP = 3, VALUE = 9, ORDER = 0}, THANKS
I'd go like follows: List<OrderRow> orders = new List<OrderRow>() { new OrderRow { ID = 1, GROUP = 1, VALUE = 10, ORDER = 0 }, new OrderRow { ID = 2, GROUP = 1, VALUE = 2, ORDER = 0 }, new OrderRow { ID = 3, GROUP = 2, VALUE = 1, ORDER = 0 }, new OrderRow { ID = 4, GROUP = 3, VALUE = 2, ORDER = 0 }, new OrderRow { ID = 5, GROUP = 3, VALUE = 6, ORDER = 0 }, new OrderRow { ID = 6, GROUP = 3, VALUE = 1, ORDER = 0 }, new OrderRow { ID = 7, GROUP = 3, VALUE = 9, ORDER = 0 }, }; foreach (var item in orders.GroupBy(order => order.GROUP)) { int order = 0; foreach (var item2 in item) { item2.ORDER = order++; } }
Get distinct Combinations of numbers using LINQ
The below code returns all distinct combinations based on the logic that 1,2,3 = 3,2,1 = 2,3,1, so it only returns 1 instance of that set of numbers. However, I want to change that logic so that it returns ALL instances of all number sets. What do I need to do to the LINQ query below "GetPowerSet" in order to make that happen? public void GetPowersets() { List<int> ints = new List<int>() { 1,2,2,3,3 }; var results = GetPowerSet(ints); List<String> combinations = new List<String>(); foreach (var result in results) { StringBuilder sb = new StringBuilder(); foreach (var intValue in result.OrderBy(x => x)) { sb.Append(intValue + ","); } combinations.Add(sb.ToString()); } string c1 = string.Join("|", combinations.ToArray()).Replace(",|", "|"); //c1 = "|1|2|1,2|2|1,2|2,2|1,2,2|3|1,3|2,3|1,2,3|2,3|1,2,3|2,2,3|1,2,2,3|3|1,3|2,3|1,2,3|2,3|1,2,3|2,2,3|1,2,2,3|3,3|1,3,3|2,3,3|1,2,3,3|2,3,3|1,2,3,3|2,2,3,3|1,2,2,3,3," } public IEnumerable<IEnumerable<int>> GetPowerSet(List<int> list) { return from m in Enumerable.Range(0, 1 << list.Count) select from i in Enumerable.Range(0, list.Count) where (m & (1 << i)) != 0 select list[i]; } This is the end result I am trying to achieve: (no duplicate rows of combinations: duplicate = 3,2,1 and 3,2,1 are the same thing. but 1,2,3 and 3,2,1 are NOT the same thing and both should be in the end result) 1 2 3 1,2 1,3 2,1 2,3 2,2 3,1 3,2 3,3 1,2,3 1,2,2 1,3,2 1,3,3 2,1,3 2,1,2 2,3,1 2,3,2 2,3,3 2,2,1 2,2,3 3,1,2 3,1,3 3,2,1 3,2,2 3,2,3 3,3,1 3,3,2 1,2,3,2 1,2,3,3 1,2,2,3 1,3,2,2 1,3,2,3 1,3,3,2 2,1,3,2 2,1,3,3 2,1,2,3 2,3,1,2 2,3,1,3 2,3,2,1 2,3,2,3 2,3,3,1 2,3,3,2 2,2,1,3 2,2,3,1 2,2,3,3 3,1,2,2 3,1,2,3 3,1,3,2 3,2,1,2 3,2,1,3 3,2,2,1 3,2,2,3 3,2,3,1 3,2,3,2 3,3,1,2 3,3,2,1 3,3,2,2 1,2,3,2,3 1,2,3,3,2 1,2,2,3,3 1,3,2,2,3 1,3,2,3,2 1,3,3,2,2 2,1,3,2,3 2,1,3,3,2 2,1,2,3,3 2,3,1,2,3 2,3,1,3,2 2,3,2,1,3 2,3,2,3,1 2,3,3,1,2 2,3,3,2,1 2,2,1,3,3 2,2,3,1,3 2,2,3,3,1 3,1,2,2,3 3,1,2,3,2 3,1,3,2,2 3,2,1,2,3 3,2,1,3,2 3,2,2,1,3 3,2,2,3,1 3,2,3,1,2 3,2,3,2,1 3,3,1,2,2 3,3,2,1,2 3,3,2,2,1 The "foreach" way of doing this, which tends to cause a "Out Of Memory Exception" once the number set gets too large (I anticipate LINQ shouldn't have this problem) is below. This works as I want it to, returning the result set I want. But it is slow and has performance issues. I'm also open to suggestions on how to make it better. public List<List<int>> GetAllCombinationsOfAllSizes(List<int> ints) { List<List<int>> returnResult = new List<List<int>>(); var distinctInts = ints.Distinct().ToList(); for (int j = 0; j < distinctInts.Count(); j++) { var number = distinctInts[j]; var newList = new List<int>(); newList.Add(number); returnResult.Add(newList); var listMinusOneObject = ints.Select(x => x).ToList(); listMinusOneObject.Remove(listMinusOneObject.Where(x => x == number).First()); if (listMinusOneObject.Count() > 0) { _GetAllCombinationsOfAllSizes(listMinusOneObject, newList, ref returnResult); } } return returnResult; } public void _GetAllCombinationsOfAllSizes(List<int> ints, List<int> growingList, ref List<List<int>> returnResult) { var distinctInts = ints.Distinct().ToList(); for (int j = 0; j < distinctInts.Count(); j++) { var number = distinctInts[j]; var newList = growingList.ToList(); newList.Add(number); returnResult.Add(newList); var listMinusOneObject = ints.Select(x => x).ToList(); listMinusOneObject.Remove(listMinusOneObject.Where(x => x == number).First()); if (listMinusOneObject.Count() > 0) { _GetAllCombinationsOfAllSizes(listMinusOneObject, newList, ref returnResult); } } } The ANSWER I am looking for is: how to achieve this result set I want , but using LINQ and C# to do it, in a way that is faster and more efficient than the current "foreach" way I have posted?
NEW UPDATE (removed old code, performance is better than OP's code, yielded output) static IEnumerable<int[]> EnumeratePermutations2(int[] ints) { Dictionary<int, int> intCounts = ints.GroupBy(n => n) .ToDictionary(g => g.Key, g => g.Count()); int[] distincts = intCounts.Keys.ToArray(); foreach (int[] permutation in EnumeratePermutations2(new int[0], intCounts, distincts)) yield return permutation; } static IEnumerable<int[]> EnumeratePermutations2(int[] prefix, Dictionary<int, int> intCounts, int[] distincts) { foreach (int n in distincts) { int[] newPrefix = new int[prefix.Length + 1]; Array.Copy(prefix, newPrefix, prefix.Length); newPrefix[prefix.Length] = n; yield return newPrefix; intCounts[n]--; int[] newDistincts = intCounts[n] > 0 ? distincts : distincts.Where(x => x != n).ToArray(); foreach (int[] permutation in EnumeratePermutations2(newPrefix, intCounts, newDistincts)) yield return permutation; intCounts[n]++; } }
I didn't touch your GetPowerSet, but instead created a SetComparer to filter the repetitions. public class SetComparer : IEqualityComparer<IEnumerable<int>> { public bool Equals(IEnumerable<int> x, IEnumerable<int> y) { return Object.ReferenceEquals(x, y) || (x != null && y != null && x.SequenceEqual(y)); } public int GetHashCode(IEnumerable<int> set) { if (set == null) return 0; //if you only want one of these 1,2,3 vs 3,2,1 //plug .OrderBy(x => x) before the Aggregate return set.Aggregate(19, (s,i) => s * 31 + i); } } Just chain .Distinct(new SetComparer()) to the results or at the end of your GetPowerSet select statement.
Based on the question. Your solution does return all result sets including duplicates. Your solutions does not exclude duplicates. Your Example output does exclude some duplicates but you list 89 sets of data. There should only be 64 with duplicates as I understand how combinations work which is 2 ^ List.Count() = 2 ^ 6 = 64 combinations Revised Answer I believe this is close to what you want. I created a short solution but I think it can be re factored and enhanced for speed. The following Link has some good Set Classes which I think should be used: http://www.codeproject.com/Articles/23391/Set-Collections-for-C The other thing I would use is the TPL Library which will allow you to speed up the processing. Link: http://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx My Result Set resulted in 190 sets. With the following code it took about 1.5 minutes to run. Main Program void Main() { var setList = new List<int>() {1,1,2,3,3,3}; var setSize = setList.Count(); var basePowerSet = PowerSet.Generate(setList); var results = PowerSet.PS; // Results generated in 1 Minute 23 seconds with no errors. var sortedSets = new SortedSet<string>(); foreach( var item in results) { sortedSets.Add(item.ToString2()); } foreach( var item in sortedSets) { Console.WriteLine(item); } } PowerSet Library public static class PowerSet { // List with no Exact Duplicates but with Permutations public static List<List<int>> PS = new List<List<int>>(); // This Method Generates the power set with No Exact Duplicates // and stores the values into the Property PS. public static List<List<int>> Generate(List<int> setList) { // Generate Base Data to use for final results var setSize = setList.Count(); var basePowerSet = from m in Enumerable.Range(0, 1 << setSize) select from i in Enumerable.Range(0, setSize) where (m & (1 << i)) != 0 select setList[i]; // Temporary Result Set with Duplicates var results = new List<List<int>>(); // Step thru each set and generate list of Permutations for each // Power Set generated above. foreach( var item in basePowerSet ) { var size = item.Count(); var positions = from m in Enumerable.Range(0, size) select m; var lItem = item.ToList(); // If the set has 2 or more elements in the set then generate Permutations switch(size) { case 0: case 1: break; default: // Permutations generated from Linq Extension defined // in Method Permute() var posList = positions.Permute().ToList(); // remove first item which is a duplicate. posList.RemoveAt(0); // Generate new Lists based on all possiable // combinations of the data in the set. var x = new List<List<int>>(); foreach(var p in posList) { var y = new List<int>(); foreach(var v in p) { //v.Dump("xxxx"); y.Add(lItem[v]); } x.Add(y); // Add New Permutation but // Do not add a duplicate set. AddNonDuplicate(x); } break; } // Add to Temp Results; results.Add(item.ToList()); // Remove Duplicates AddNonDuplicate(results); } return results; } // Custom Method used to compare values in a set to the // Final Result Set named PS. public static void AddNonDuplicate(List<List<int>> list ) { //list.Dump(); if(list.Count() == 0) return; foreach(var item in list) { bool found = false; var mySize = PS.Count(); if(mySize <= 0) PS.Add(item); else foreach(var psItem in PS) { if( item.ToString2() == psItem.ToString2() ) found = true; } if(!found) PS.Add(item); } } } Extension Library // My Extension Methods public static class MyExt { public static IEnumerable<IEnumerable<T>> Permute<T>(this IEnumerable<T> list) { if (list.Count() == 1) return new List<IEnumerable<T>> { list }; return list .Select((a, i1) => Permute(list.Where((b, i2) => i2 != i1)) .Select(b => (new List<T> { a }).Union(b))) .SelectMany(c => c); } public static string ToString2<T>(this List<T> list) { StringBuilder results = new StringBuilder("{ "); var size = list.Count(); var pos = 1; foreach( var i in list ) { results.Append(i.ToString()); if(pos++!=size) results.Append(", "); } results.Append(" }"); return results.ToString().Trim(','); } } Results { } { 1 } { 1, 1 } { 1, 1, 2 } { 1, 1, 2, 3 } { 1, 1, 2, 3, 3 } { 1, 1, 2, 3, 3, 3 } { 1, 1, 3 } { 1, 1, 3, 2 } { 1, 1, 3, 2, 3 } { 1, 1, 3, 2, 3, 3 } { 1, 1, 3, 3 } { 1, 1, 3, 3, 2 } { 1, 1, 3, 3, 2, 3 } { 1, 1, 3, 3, 3 } { 1, 1, 3, 3, 3, 2 } { 1, 2 } { 1, 2, 1 } { 1, 2, 1, 3 } { 1, 2, 1, 3, 3 } { 1, 2, 1, 3, 3, 3 } { 1, 2, 3 } { 1, 2, 3, 1 } { 1, 2, 3, 1, 3 } { 1, 2, 3, 1, 3, 3 } { 1, 2, 3, 3 } { 1, 2, 3, 3, 1 } { 1, 2, 3, 3, 1, 3 } { 1, 2, 3, 3, 3 } { 1, 2, 3, 3, 3, 1 } { 1, 3 } { 1, 3, 1 } { 1, 3, 1, 2 } { 1, 3, 1, 2, 3 } { 1, 3, 1, 2, 3, 3 } { 1, 3, 1, 3 } { 1, 3, 1, 3, 2 } { 1, 3, 1, 3, 2, 3 } { 1, 3, 1, 3, 3 } { 1, 3, 1, 3, 3, 2 } { 1, 3, 2 } { 1, 3, 2, 1 } { 1, 3, 2, 1, 3 } { 1, 3, 2, 1, 3, 3 } { 1, 3, 2, 3 } { 1, 3, 2, 3, 1 } { 1, 3, 2, 3, 1, 3 } { 1, 3, 2, 3, 3 } { 1, 3, 2, 3, 3, 1 } { 1, 3, 3 } { 1, 3, 3, 1 } { 1, 3, 3, 1, 2 } { 1, 3, 3, 1, 2, 3 } { 1, 3, 3, 1, 3 } { 1, 3, 3, 1, 3, 2 } { 1, 3, 3, 2 } { 1, 3, 3, 2, 1 } { 1, 3, 3, 2, 1, 3 } { 1, 3, 3, 2, 3 } { 1, 3, 3, 2, 3, 1 } { 1, 3, 3, 3 } { 1, 3, 3, 3, 1 } { 1, 3, 3, 3, 1, 2 } { 1, 3, 3, 3, 2 } { 1, 3, 3, 3, 2, 1 } { 2 } { 2, 1 } { 2, 1, 1 } { 2, 1, 1, 3 } { 2, 1, 1, 3, 3 } { 2, 1, 1, 3, 3, 3 } { 2, 1, 3 } { 2, 1, 3, 1 } { 2, 1, 3, 1, 3 } { 2, 1, 3, 1, 3, 3 } { 2, 1, 3, 3 } { 2, 1, 3, 3, 1 } { 2, 1, 3, 3, 1, 3 } { 2, 1, 3, 3, 3 } { 2, 1, 3, 3, 3, 1 } { 2, 3 } { 2, 3, 1 } { 2, 3, 1, 1 } { 2, 3, 1, 1, 3 } { 2, 3, 1, 1, 3, 3 } { 2, 3, 1, 3 } { 2, 3, 1, 3, 1 } { 2, 3, 1, 3, 1, 3 } { 2, 3, 1, 3, 3 } { 2, 3, 1, 3, 3, 1 } { 2, 3, 3 } { 2, 3, 3, 1 } { 2, 3, 3, 1, 1 } { 2, 3, 3, 1, 1, 3 } { 2, 3, 3, 1, 3 } { 2, 3, 3, 1, 3, 1 } { 2, 3, 3, 3 } { 2, 3, 3, 3, 1 } { 2, 3, 3, 3, 1, 1 } { 3 } { 3, 1 } { 3, 1, 1 } { 3, 1, 1, 2 } { 3, 1, 1, 2, 3 } { 3, 1, 1, 2, 3, 3 } { 3, 1, 1, 3 } { 3, 1, 1, 3, 2 } { 3, 1, 1, 3, 2, 3 } { 3, 1, 1, 3, 3 } { 3, 1, 1, 3, 3, 2 } { 3, 1, 2 } { 3, 1, 2, 1 } { 3, 1, 2, 1, 3 } { 3, 1, 2, 1, 3, 3 } { 3, 1, 2, 3 } { 3, 1, 2, 3, 1 } { 3, 1, 2, 3, 1, 3 } { 3, 1, 2, 3, 3 } { 3, 1, 2, 3, 3, 1 } { 3, 1, 3 } { 3, 1, 3, 1 } { 3, 1, 3, 1, 2 } { 3, 1, 3, 1, 2, 3 } { 3, 1, 3, 1, 3 } { 3, 1, 3, 1, 3, 2 } { 3, 1, 3, 2 } { 3, 1, 3, 2, 1 } { 3, 1, 3, 2, 1, 3 } { 3, 1, 3, 2, 3 } { 3, 1, 3, 2, 3, 1 } { 3, 1, 3, 3 } { 3, 1, 3, 3, 1 } { 3, 1, 3, 3, 1, 2 } { 3, 1, 3, 3, 2 } { 3, 1, 3, 3, 2, 1 } { 3, 2 } { 3, 2, 1 } { 3, 2, 1, 1 } { 3, 2, 1, 1, 3 } { 3, 2, 1, 1, 3, 3 } { 3, 2, 1, 3 } { 3, 2, 1, 3, 1 } { 3, 2, 1, 3, 1, 3 } { 3, 2, 1, 3, 3 } { 3, 2, 1, 3, 3, 1 } { 3, 2, 3 } { 3, 2, 3, 1 } { 3, 2, 3, 1, 1 } { 3, 2, 3, 1, 1, 3 } { 3, 2, 3, 1, 3 } { 3, 2, 3, 1, 3, 1 } { 3, 2, 3, 3 } { 3, 2, 3, 3, 1 } { 3, 2, 3, 3, 1, 1 } { 3, 3 } { 3, 3, 1 } { 3, 3, 1, 1 } { 3, 3, 1, 1, 2 } { 3, 3, 1, 1, 2, 3 } { 3, 3, 1, 1, 3 } { 3, 3, 1, 1, 3, 2 } { 3, 3, 1, 2 } { 3, 3, 1, 2, 1 } { 3, 3, 1, 2, 1, 3 } { 3, 3, 1, 2, 3 } { 3, 3, 1, 2, 3, 1 } { 3, 3, 1, 3 } { 3, 3, 1, 3, 1 } { 3, 3, 1, 3, 1, 2 } { 3, 3, 1, 3, 2 } { 3, 3, 1, 3, 2, 1 } { 3, 3, 2 } { 3, 3, 2, 1 } { 3, 3, 2, 1, 1 } { 3, 3, 2, 1, 1, 3 } { 3, 3, 2, 1, 3 } { 3, 3, 2, 1, 3, 1 } { 3, 3, 2, 3 } { 3, 3, 2, 3, 1 } { 3, 3, 2, 3, 1, 1 } { 3, 3, 3 } { 3, 3, 3, 1 } { 3, 3, 3, 1, 1 } { 3, 3, 3, 1, 1, 2 } { 3, 3, 3, 1, 2 } { 3, 3, 3, 1, 2, 1 } { 3, 3, 3, 2 } { 3, 3, 3, 2, 1 } { 3, 3, 3, 2, 1, 1 }
Group by using linq (range + count)
var data = new[] { new { Id = 0, Cat = 1, Price = 2 }, new { Id = 1, Cat = 1, Price = 10 }, new { Id = 2, Cat = 1, Price = 30 }, new { Id = 3, Cat = 2, Price = 50 }, new { Id = 4, Cat = 2, Price = 120 }, new { Id = 5, Cat = 2, Price = 200 }, new { Id = 6, Cat = 2, Price = 1024 }, }; var ranges = new[] { 10, 50, 100, 500 }; Needed output is grouped price count by equal or greater than the range used according categories. (in one linq statement) cat range count ------------------------------------- 1 10 2 (In 1. categories there is 2 item that price >= 10(range) [10;30]) 2 10 4 (In 2. categories there is 4 item that price >= 10(range) [50;120;200;1024]) 2 50 4 .... 2 100 3 .... 2 500 1 (In 2. categories there is 1 item that price >= 500(range) [1024])
Try this: var data = new[] { new { Id = 0, Cat = 1, Price = 2 }, new { Id = 1, Cat = 1, Price = 10 }, new { Id = 2, Cat = 1, Price = 30 }, new { Id = 3, Cat = 2, Price = 50 }, new { Id = 4, Cat = 2, Price = 120 }, new { Id = 5, Cat = 2, Price = 200 }, new { Id = 6, Cat = 2, Price = 1024 }, }; var ranges = new[] { 10, 50, 100, 500 }; var result = from r in ranges from g in data where g.Price >= r select new {g.Cat, Price=r}; var groupedData = from d in result group d by new{d.Cat, d.Price} into g select new{Cat=g.Key.Cat, Price=g.Key.Price, TotalCount=g.Count()};
This should work: var values = data.SelectMany(x => ranges.Where(y => x.Price >= y) .Select(y => new { Record = x, Range = y })) .GroupBy(x => new { Cat = x.Record.Cat, Range = x.Range }) .Select(x => new { Cat = x.Key.Cat, Range = x.Key.Range, Count = x.Count()}); Results: { Cat = 1, Range = 10, Count = 2 } { Cat = 2, Range = 10, Count = 4 } { Cat = 2, Range = 50, Count = 4 } { Cat = 2, Range = 100, Count = 3 } { Cat = 2, Range = 500, Count = 1 }
Sub Totalling Relational Datatable
I have a datatable with relational data structure, I need to sum up the sub nodes to their parent nodes all the way up to the top parent (NULL parent Id) I have attached 2 images which shows the original table and another with the expected results Cheers
I've taken an approach that simulates data as they could have been materialized from a database by some ORM, i.e. a class that contains data and a collection of children. Plus some "business logic" to calculate the required numbers. So you can choose a db approach as well as an in-memory approach. In Linqpad: void Main() { var data = new[] { new Record { Id = 1, ParentId = null, Qty = 1, Cost = 0.0m }, new Record { Id = 2, ParentId = 1, Qty = 2, Cost = 0.0m }, new Record { Id = 3, ParentId = 1, Qty = 3, Cost = 0.0m }, new Record { Id = 4, ParentId = 2, Qty = 4, Cost = 0.0m }, new Record { Id = 5, ParentId = 3, Qty = 5, Cost = 0.0m }, new Record { Id = 6, ParentId = 2, Qty = 6, Cost = 1.7m }, new Record { Id = 7, ParentId = 4, Qty = 7, Cost = 1.8m }, new Record { Id = 8, ParentId = 5, Qty = 8, Cost = 1.9m }, new Record { Id = 9, ParentId = 5, Qty = 9, Cost = 2.0m }, }.ToList(); // Mimic ORM's job: data.ForEach(d => d.ChildRecords = data.Where(c => c.ParentId == d.Id).ToList()); data.Select(d => new { d.Id, d.Cost, d.TotalCost } ).Dump(); } class Record { public int Id { get; set; } public int? ParentId { get; set; } public int Qty { get; set; } private decimal _cost = 0m; public decimal Cost { get { return this._cost + this.ChildRecords.Sum(cr => cr.TotalCost); } set { this._cost = value; } } public decimal TotalCost { get { return this.Qty * this.Cost; } } public ICollection<Record> ChildRecords; } Result: Id Cost TotalCost 1 619.2 619.2 2 60.6 121.2 3 166 498 4 12.6 50.4 5 33.2 166 6 1.7 10.2 7 1.8 12.6 8 1.9 15.2 9 2 18 An optimization could be to apply some memoization, i.e. let the Cost property store the result of its getter in a private member variable.
Selecting "custom distinct" items from a List using LINQ
I have a generic List of Policy objects. The list contains the following data id policyNumber policySequence otherData 1 101 1 aaaa 2 101 2 bbbb 3 101 3 cccc 4 102 1 dddd 5 103 1 eeee 6 103 2 ffff I want to select the one row containing the highest policySequence for each policyNumber, so that I end up with the following: id policyNumber policySequence created 3 101 3 cccc 4 102 1 dddd 6 103 2 ffff I have a solution below using a foreach, but was wondering if there was an easier, cleaner way to do this in LINQ? class Program { static void Main(string[] args) { List<Policy> policyList = new List<Policy> { new Policy {id = 1, policyNumber = 101, policySequence = 1, otherData = "aaaa"}, new Policy {id = 2, policyNumber = 101, policySequence = 2, otherData = "bbbb"}, new Policy {id = 3, policyNumber = 101, policySequence = 3, otherData = "cccc"}, new Policy {id = 4, policyNumber = 102, policySequence = 1, otherData = "dddd"}, new Policy {id = 5, policyNumber = 103, policySequence = 1, otherData = "eeee"}, new Policy {id = 6, policyNumber = 103, policySequence = 2, otherData = "ffff"} }; List<Policy> filteredPolicyList = new List<Policy>(); foreach(var policy in policyList) { if(!filteredPolicyList.Exists(x => x.policyNumber == policy.policyNumber)) { filteredPolicyList.Add(policy); } else { var currentPolicyInFilteredList = filteredPolicyList.Where(x => x.policyNumber == policy.policyNumber).First(); if (policy.policySequence > currentPolicyInFilteredList.policySequence) { filteredPolicyList.Remove(currentPolicyInFilteredList); filteredPolicyList.Add(policy); } } } } } public class Policy { public int id; public int policyNumber; public int policySequence; public string otherData; }
var maxPolicies = policyList .GroupBy(p => p.PolicyNumber) .Select(grp => grp.OrderByDescending(p => p.PolicySequence).First());
If you're using LINQ to Objects, you could use the MoreLINQ project's DistinctBy method: var maxPolicies = policyList.OrderByDescending(x => x.PolicySequence) .DistinctBy(x => x.PolicyNumber);
You could group and aggregate: var result = from p in policyList group p by p.policyNumber into g select new { Policy = g.Key, Max = g.Max() };