Flatten LINQ collection object with nested object collections - linq

This is a tricky one. I an trying to flatten a LINQ object collection. Each item in the collection has the potential of having two collections of other objects. See the example below.
public class DemoClass
{
public string Name {get; set;}
public string Address {get; set;}
public List<Foo> Foos = new List<Foo>();
public List<Bar> Bars = new List<Bars>();
}
What I had been doing is this using this code block to flatten this object
var output = from d in DemoClassCollection
from f in d.Foos
from b in d.Bars
select new {
d.Name,
d.Address,
f.FooField1,
f.FooField2,
b.BarField1,
b.BarField2
};
But the problem I'm having is that the result I get is only those DemoClass objects that have objects in the Foos and Bars collections. I need to get all objects in the DemoClass regardless if there are objects in the Foos and Bars collections.
Any help would be greatly appreciated.
Thanks!

Sounds like you might want to use DefaultIfEmpty:
var output = from d in DemoClassCollection
from f in d.Foos.DefaultIfEmpty()
from b in d.Bars.DefaultIfEmpty()
select new {
d.Name,
d.Address,
FooField1 = f == null ? null : f.FooField1,
FooField2 = f == null ? null : f.FooField2,
BarField1 = b == null ? null : b.BarField1,
BarField2 = b == null ? null : b.BarField2
};

Looks like a left outer join in Linq will work (http://msdn.microsoft.com/en-us/library/bb397895.aspx
var output = from d in DemoClassCollection
from f in d.Foos.DefaultIfEmpty()
from b in d.Bars.DefaultIfEmpty()
select new {
d.Name,
d.Address,
f.FooField1,
f.FooField2,
b.BarField1,
b.BarField2
};

I believe you can implement an IComparer to perform custom JOINS or UNIONS in linq based on how you implement the CompareTo() method
From MSDN: http://msdn.microsoft.com/en-us/library/system.icomparable.aspx
using System;
using System.Collections;
public class Temperature : IComparable
{
// The temperature value
protected double temperatureF;
public int CompareTo(object obj) {
if (obj == null) return 1;
Temperature otherTemperature = obj as Temperature;
if (otherTemperature != null)
return this.temperatureF.CompareTo(otherTemperature.temperatureF);
else
throw new ArgumentException("Object is not a Temperature");
}
public double Fahrenheit
{
get
{
return this.temperatureF;
}
set {
this.temperatureF = value;
}
}
public double Celsius
{
get
{
return (this.temperatureF - 32) * (5.0/9);
}
set
{
this.temperatureF = (value * 9.0/5) + 32;
}
}
}
public class CompareTemperatures
{
public static void Main()
{
ArrayList temperatures = new ArrayList();
// Initialize random number generator.
Random rnd = new Random();
// Generate 10 temperatures between 0 and 100 randomly.
for (int ctr = 1; ctr <= 10; ctr++)
{
int degrees = rnd.Next(0, 100);
Temperature temp = new Temperature();
temp.Fahrenheit = degrees;
temperatures.Add(temp);
}
// Sort ArrayList.
temperatures.Sort();
foreach (Temperature temp in temperatures)
Console.WriteLine(temp.Fahrenheit);
}
}
// The example displays the following output to the console (individual
// values may vary because they are randomly generated):
// 2
// 7
// 16
// 17
// 31
// 37
// 58
// 66
// 72
// 95

Related

List Distinct is not working in LINQ instead of GroupBy

Thanks in advance. I can get required output when using var but i want to get required output by using Distinct in List<>.
InventoryDetails.cs
public class InventoryDetails
{
public int? PersonalInventoryGroupId { get; set; }
public int? PersonalInventoryBinId { get; set; }
}
InventoryController.cs
[HttpGet("GetInventory")]
public IActionResult GetInventory(int id)
{
//Below code will return distinct record
var inventory = (from i in _context.TempTbl
where i.TempId == id
select new
{
PersonalInventoryBinId = i.PersonalInventoryBinId,
PersonalInventoryGroupId = i.PersonalInventoryGroupId,
}).ToList().Distinct().ToList();
//Below code is not doing distinct
List<InventoryDetails> inventory = (from i in _context.TempTbl
where i.TempId == id
select new InventoryDetails
{
PersonalInventoryBinId = i.PersonalInventoryBinId,
PersonalInventoryGroupId = i.PersonalInventoryGroupId,
}).ToList().Distinct().ToList();
}
If i use var as return type, then i am able to get distinct records. Could some one assist it.
Please try like this it may help.
IList<InventoryDetails> inventory = _context.InventoryDetails.Where(x=>x.TempId == id).GroupBy(p => new {p.PersonalInventoryGroupId, p.PersonalInventoryBinId } )
.Select(g => g.First())
.ToList();
You need to override Equals and GetHashCode.
First, let's see the AnonymousType vs InventoryDetails
var AnonymousTypeObj1 = new { PersonalInventoryGroupId = 1, PersonalInventoryBinId = 1 };
var AnonymousTypeObj2 = new { PersonalInventoryGroupId = 1, PersonalInventoryBinId = 1 };
Console.WriteLine(AnonymousTypeObj1.Equals(AnonymousTypeObj2)); // True
var InventoryDetailsObj1 = new InventoryDetails { PersonalInventoryBinId = 1, PersonalInventoryGroupId = 1 };
var InvertoryDetailsObj2 = new InventoryDetails { PersonalInventoryBinId = 1, PersonalInventoryGroupId = 1 };
Console.WriteLine(InventoryDetailsObj1.Equals(InvertoryDetailsObj2)); // False
You can see the Equals behave differently which make Distinct behave differently. The problem is not var you mentioned in your question but AnonoymizeType
To make Distinct works as you expect, you need to override Equals and GetHashCode
public class InventoryDetails
{
public int? PersonalInventoryGroupId { get; set; }
public int? PersonalInventoryBinId { get; set; }
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj is InventoryDetails)
{
if (PersonalInventoryGroupId == (obj as InventoryDetails).PersonalInventoryGroupId
&& PersonalInventoryBinId == (obj as InventoryDetails).PersonalInventoryBinId)
return true;
}
return false;
}
public override int GetHashCode()
{
int hash = 17;
hash = hash * 23 + PersonalInventoryBinId.GetHashCode();
hash = hash * 23 + PersonalInventoryGroupId.GetHashCode();
return hash;
}
}
Another approach would be
List<InventoryDetails> inventory = (from i in TempTbl
where i.TempId == id
select new InventoryDetails
{
PersonalInventoryBinId = i.PersonalInventoryBinId,
PersonalInventoryGroupId = i.PersonalInventoryGroupId,
}).AsQueryable().ToList().Distinct(new customComparer()).ToList();
public class customComparer:IEqualityComparer<InventoryDetails>
{
public bool Equals(InventoryDetails x, InventoryDetails y)
{
if (x.TempId == y.TempId && x.PersonalInventoryBinId == y.PersonalInventoryBinId
&& x.PersonalInventoryGroupId == y.PersonalInventoryGroupId)
{
return true;
}
return false;
}
public int GetHashCode(InventoryDetails obj)
{
return string.Concat(obj.PersonalInventoryBinId.ToString(),
obj.PersonalInventoryGroupId.ToString(),
obj.TempId.ToString()).GetHashCode();
}
}
As said in a comment by Ivan, you make your life difficult by calling ToList before Distinct. This prevents the SQL provider from incorporating the Distinct call into the generated SQL statement. But that leaves the question: what causes the difference?
The first query generates anonymous type instances. As per the C# specification, by default anonymous types (in C#) are equal when their properties and property values are equal (structural equality). Conversely, by default, reference types (like InventoryDetails) are equal when their reference (say memory address) is equal (reference equality or identity). They can be made equal by overriding their Equals and GetHashcode methods, as some people suggested to do.
But that's not necessary if you remove the first ToList():
var inventory = (from i in _context.TempTbl
where i.TempId == id
select new InventoryDetails
{
PersonalInventoryBinId = i.PersonalInventoryBinId,
PersonalInventoryGroupId = i.PersonalInventoryGroupId,
}).Distinct().ToList();
Now the whole statement until ToList() is an IQueryable that can be translated into SQL. The SQL is executed and the database returns a distinct result set of raw records from which EF materializes InventoryDetails objects. The C# runtime code was even never aware of duplicates!

Relative complement of two lists where the elements are of different types

I'm looking to remove all elements in a list that can be compared to elements of another list of a different type that don't share a common inheretence, but I do have an equality function for. An example might make it clearer:
given the scaffolding
bool isSomeSortOfEqual(Bottle b, Printer p){
//implementation
}
List<Bottle> bottles = getBottles();
List<Printer> printers = getPrinters();
I would like to do something like this:
List<Bottle> result = bottles.Except(printers, (b, p => isSomeSortOfEqual(b, p));
Are there any builtins for this in .NET, or should I implement this by hand? None of the questions relating to relative complement or except in .NET on stackoverflow seem to deal with having different types.
How about this? The basic idea is to cast the lists to List<object> and then use .Except with an IEqualityComparer<object>
class A
{
public int Ai;
}
class B
{
public int Bi;
}
public class ABComparer : IEqualityComparer<object>
{
public bool Equals(object x, object y)
{
A isA = x as A ?? y as A;
B isB = x as B ?? y as B;
if (isA == null || isB == null)
return false;
return isA.Ai == isB.Bi;
}
public int GetHashCode(object obj)
{
A isA = obj as A;
if (isA != null)
return isA.Ai;
B isB = obj as B;
if (isB != null)
return isB.Bi;
return obj.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
List<object> As = new List<object> { new A { Ai = 1 }, new A { Ai = 2 }, new A { Ai = 3 } };
List<object> Bs = new List<object> { new B { Bi = 1 }, new B { Bi = 1 } };
var except = As.Except(Bs, new ABComparer()).ToArray();
// Will give two As with Ai = 2 and Ai = 3
}
}
not any matches?
from b in bottles
where !printers.Any(p => isSomeSortOfEqual(b, p))
select b;

Getting Unique Combinations Of Particular Table?

Table1:
ValueA
ValueB
ValueC
Example Data:
1,2,3
1,2,4
1,2,4
1,2,4
1,5,6
1,5,6
I want to get the unique rows based on these three values:
1,2,3
1,2,4
1,5,6
How can this be done most easily using linq?
Write your own IEqualityComparer and use Enumerable.Distinct on a collection of your objects representing the rows.
Something like (sorry, did not test in a compiler):
class Foo {
public int ValueA { get; set; }
public int ValueB { get; set; }
public int ValueC { get; set; }
}
class FooEqualityComparer : IEqualityComparer<Foo> {
public bool Equals(Foo x, Foo y) {
if(Object.ReferenceeEquals(x, y)) { return true; }
if(x == null || y == null) { return false; }
return x.ValueA == y.ValueA &&
x.ValueB == y.ValueB &&
x.ValueC == y.ValueC;
}
public int GetHashCode(Foo obj) {
if(obj == null) { return 0; }
unchecked {
int hashCode = 17;
hashCode = hashCode * 23 + obj.ValueA.GetHashCode();
hashCode = hashCode * 23 + obj.ValueB.GetHashCode();
hashCode = hashCode * 23 + obj.ValueC.GetHashCode();
return hashCode;
}
}
}
Then:
IEnumerable<Foo> foos = // some foos;
var distinct = foos.Distinct(new FooEqualityComparer());
you could use .Distinct() with your own comparer class
class MyComparer : IEqualityComparer<YourRowClass>
then use it like
yourList.Distinct(MyComparer())

Aggregate function over an aggregate result set using linq

I have the following linq query:
var totalAmountsPerMonth =
from s in Reports()
where s.ReportDate.Value.Year == year
group s by s. ReportDate.Value.Month into g
orderby g.Key
select new
{
month = g.Key,
totalRecaudacion = g.Sum(rec => rec.RECAUDACION),
totalServicios = g.Sum(ser => ser.SERVICIOS)
};
var final = new ResultSet
{
Recaudacion = meses.Average(q => q. totalRecaudacion),
Servicios = meses.Average(o => o. totalServicios)
};
And I need to obtain the average of the total amount of “RECAUDACION” and “SERVICIOS” of each month. I made this query. However, I definitely think this is not the best solution at all. Could you please suggest me a better and more efficient approach (in a single query if possible) to get these data?
I have created a simple extension method. And it turns out to be two times more efficient in a simple stopwatch benchmark.
public class Report
{
public DateTime? Date { get; set; }
public int RECAUDACION { get; set; }
public int SERVICIOS { get; set; }
}
static class EnumerableEx
{
public static Tuple<double, double> AveragePerMonth(this IEnumerable<Report> reports)
{
var months = new HashSet<int>();
double RECAUDACION = 0d;
double SERVICIOS = 0d;
foreach (Report rep in reports)
{
if (!months.Contains(rep.Date.Value.Month))
{
months.Add(rep.Date.Value.Month);
}
RECAUDACION += rep.RECAUDACION;
SERVICIOS += rep.SERVICIOS;
}
var totalMonth = months.Count;
if (months.Count > 0)
{
RECAUDACION /= totalMonth;
SERVICIOS /= totalMonth;
}
return Tuple.Create<double, double>(RECAUDACION, SERVICIOS);
}
}

Sum of hierarchical data using LINQ?

Is it possible to sum hierarchical data using .NET's LINQ?
My data class looks like this:
class Node
{
public decimal Amount;
public IEnumerable<Node> Children { get; set; }
}
So I would have some data looks like this, but the tree could of course be arbitrarily deep.
var amounts = new Node
{
Amount = 10;
Children = new[]
{
new Node
{
Amount = 20
},
new Node
{
Amount = 30
}
}
};
It is possible sum all the amounts and get the result 60 with one simple LINQ query?
You can do it with a higher order function:
Func<Node, decimal> summer = null;
summer = node => node.Amount +
(node.Children == null ? 0m : node.Children.Sum(summer));
decimal total = summer(amounts);
Note that if you can ensure that node.Children will never be null, summer can be simpler:
summer = node => node.Amount + node.Children.Sum(summer);
Alternatively, you could use the null coalescing operator:
summer = node => node.Amount +
(node.Children ?? Enumerable.Empty<Node>()).Sum(summer);
Of course you could put this into a separate method instead:
static decimal SumNodes(Node node)
{
return node.Amount +
(node.Children ?? Enumerable.Empty<Node>())
.Sum((Func<Node, decimal>)SumNodes);
}
Note the ugliness here is due to an ambiguity in method group conversions. Method groups don't get much love in type inference.
and then call SumNodes(amount). Lots of options :)
Full example of the first form:
using System;
using System.Collections.Generic;
using System.Linq;
class Node
{
public decimal Amount;
public IEnumerable<Node> Children { get; set; }
}
public class Test
{
static void Main()
{
var amounts = new Node {
Amount = 10, Children = new[] {
new Node { Amount = 20 },
new Node { Amount = 30 }
}
};
Func<Node, decimal> summer = null;
summer = node => node.Amount +
(node.Children == null ? 0m : node.Children.Sum(summer));
decimal total = summer(amounts);
Console.WriteLine(total);
}
}
I'm not sure I'd call any of these a "simple" LINQ query, mind you...
Technically you can write recursive lambda expressions, but you need to be insane or insanely bright to try (I haven't figured out which). But you can cheat:
Func<Node, decimal> nodeSum = null;
nodeSum = node => {
decimal result = node.Amount;
if (node.Children != null) {
result = result + node.Children.Sum(nodeSum);
}
return result;
};
var value = nodeSum(amounts);

Resources