Created
October 18, 2012 15:09
-
-
Save zaus/3912453 to your computer and use it in GitHub Desktop.
C# Linq Sublevel Filtering - Only return 1st-level with matching 2nd-level items, but resultset only contains matching 2nd-level items
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LinqSubsetTests { | |
/// example class | |
public class Foo { | |
public long ID { get; set; } | |
public int Filterable { get; set; } | |
public string Name { get; set; } | |
public IEnumerable<Bar> Children { get; set; } | |
public override string ToString() { | |
return string.Format(@"Foo[I:{3}, F:{0}, N:{1}; C: | |
{2} | |
]" | |
, this.Filterable | |
, this.Name | |
, string.Join(@", | |
", this.Children.Select(o => o.ToString())) | |
, this.ID | |
); | |
} | |
} | |
/// another example class | |
public class Bar { | |
public long ID { get; set; } | |
public long ParentID { get; set; } | |
public int Filterable { get; set; } | |
public string Name { get; set; } | |
public virtual Foo Parent { get; set; } | |
public override string ToString() { | |
return string.Format("Bar[I: {2}, F:{0}, N:{1}; P:{3}]" | |
, this.Filterable | |
, this.Name | |
, this.ID | |
, this.ParentID | |
); | |
} | |
} | |
protected static Random Rand = new Random(); | |
protected const int MAX_VALUE = 5; | |
/// <summary> | |
/// for calculating "guid" | |
/// </summary> | |
private static int guid_counter = 1; | |
/// <summary> | |
/// Randomized Foo list | |
/// </summary> | |
/// <param name="size"></param> | |
/// <returns></returns> | |
protected static List<Foo> MakeFooList(int size) { | |
var results = new List<Foo>(); | |
long id; | |
while (size-- > 0) { | |
var newitem = new Foo { | |
ID = guid_counter++ | |
, Filterable = Rand.Next(1, MAX_VALUE) | |
, Name = string.Format("Foo{0}", size) | |
}; | |
// recursive attach | |
newitem.Children = MakeBarList(Rand.Next(2, 5), newitem); | |
results.Add(newitem); | |
} | |
return results; | |
} | |
/// <summary> | |
/// Randomized Bar list | |
/// </summary> | |
/// <param name="size"></param> | |
/// <returns></returns> | |
protected static List<Bar> MakeBarList(int size, Foo parent) { | |
var results = new List<Bar>(); | |
while (size-- > 0) { | |
results.Add(new Bar { | |
ID = guid_counter++ | |
, ParentID = parent.ID | |
, Parent = parent | |
, Filterable = Rand.Next(1, MAX_VALUE) | |
, Name = string.Format("Bar{0}", size) | |
}); | |
} | |
return results; | |
} | |
/// <summary> | |
/// Actual "Tests": Filter on filterable column | |
/// </summary> | |
/// <param name="filter"></param> | |
public static void GetForSubset() { | |
int filter = Rand.Next(1, MAX_VALUE); | |
var foos = MakeFooList(5); | |
// debug output to review | |
foos.ForEach(o => Program.Out(o.ToString())); | |
Program.Out("==== Filtering on {0} ====", filter); | |
var filtered1 = foos.Where(o => o.Filterable == filter).ToList(); | |
// debug output to review | |
Program.Out("==== Filtered on Foo ===="); | |
filtered1.ForEach(o => Program.Out(o.ToString())); | |
var filtered2 = foos.Where(o => o.Children.Any(o2 => o2.Filterable == filter)).ToList(); | |
// debug output to review | |
Program.Out("==== Filtered Foo on Bar ===="); | |
filtered2.ForEach(o => Program.Out(o.ToString())); | |
var filtered3 = foos | |
/* ^ "table" of Foo */.SelectMany(o => o.Children) | |
/* ^ simulated table of Bar */.Where(o => o.Filterable == filter) | |
/* ^ only matching items */.GroupBy(o => o.Parent) | |
/* ^ grouped by top-level */.Select(o => | |
// reconstruct top level | |
new Foo { | |
ID = o.Key.ID, | |
Filterable = o.Key.Filterable, | |
Name = o.Key.Name, | |
// filter sublevel | |
Children = o.Where(o2 => o2.Filterable == filter) | |
}).ToList(); | |
// debug output to review | |
Program.Out("==== Filtered Foo by Bar ===="); | |
filtered3.ForEach(o => Program.Out(o.ToString())); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
============== SAMPLE RESULTS ==================== | |
Foo[I:1, F:1, N:Foo4; C: | |
Bar[I: 2, F:1, N:Bar2; P:1], | |
Bar[I: 3, F:3, N:Bar1; P:1], | |
Bar[I: 4, F:4, N:Bar0; P:1] | |
] | |
Foo[I:5, F:4, N:Foo3; C: | |
Bar[I: 6, F:2, N:Bar3; P:5], | |
Bar[I: 7, F:3, N:Bar2; P:5], | |
Bar[I: 8, F:1, N:Bar1; P:5], | |
Bar[I: 9, F:3, N:Bar0; P:5] | |
] | |
Foo[I:10, F:3, N:Foo2; C: | |
Bar[I: 11, F:1, N:Bar1; P:10], | |
Bar[I: 12, F:4, N:Bar0; P:10] | |
] | |
Foo[I:13, F:2, N:Foo1; C: | |
Bar[I: 14, F:2, N:Bar2; P:13], | |
Bar[I: 15, F:2, N:Bar1; P:13], | |
Bar[I: 16, F:1, N:Bar0; P:13] | |
] | |
Foo[I:17, F:4, N:Foo0; C: | |
Bar[I: 18, F:1, N:Bar1; P:17], | |
Bar[I: 19, F:2, N:Bar0; P:17] | |
] | |
==== Filtering on 4 ==== | |
==== Filtered on Foo ==== | |
Foo[I:5, F:4, N:Foo3; C: | |
Bar[I: 6, F:2, N:Bar3; P:5], | |
Bar[I: 7, F:3, N:Bar2; P:5], | |
Bar[I: 8, F:1, N:Bar1; P:5], | |
Bar[I: 9, F:3, N:Bar0; P:5] | |
] | |
Foo[I:17, F:4, N:Foo0; C: | |
Bar[I: 18, F:1, N:Bar1; P:17], | |
Bar[I: 19, F:2, N:Bar0; P:17] | |
] | |
==== Filtered Foo on Bar ==== | |
Foo[I:1, F:1, N:Foo4; C: | |
Bar[I: 2, F:1, N:Bar2; P:1], | |
Bar[I: 3, F:3, N:Bar1; P:1], | |
Bar[I: 4, F:4, N:Bar0; P:1] | |
] | |
Foo[I:10, F:3, N:Foo2; C: | |
Bar[I: 11, F:1, N:Bar1; P:10], | |
Bar[I: 12, F:4, N:Bar0; P:10] | |
] | |
==== Filtered Foo by Bar ==== | |
Foo[I:1, F:1, N:Foo4; C: | |
Bar[I: 4, F:4, N:Bar0; P:1] | |
] | |
Foo[I:10, F:3, N:Foo2; C: | |
Bar[I: 12, F:4, N:Bar0; P:10] | |
] | |
-------------- | |
(Press any key to continue) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment