Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
C# Linq Sublevel Filtering - Only return 1st-level with matching 2nd-level items, but resultset only contains matching 2nd-level items
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()));
}
}
============== 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
You can’t perform that action at this time.