-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHierarchicalLinqExtensions.cs
241 lines (219 loc) · 14.8 KB
/
HierarchicalLinqExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
using System;
using System.Collections.Generic;
using System.Linq;
using HLinq.Utility;
namespace HLinq
{
/// <summary>
/// Linq Extension Methods
/// </summary>
public static class HierarchicalLinqExtensions
{
/// <summary>
/// Under normal circumstances, it is assumed that a single parent will be returned for a particular object. If there are more matches an exception will be thrown.
/// This will select only that object, or return the object's default value.
/// </summary>
/// <typeparam name="T">The type of object.</typeparam>
/// <param name="source">The list of objects to traverse</param>
/// <param name="target">The context object in which you want to find a related object from</param>
/// <param name="parentPredicate">The predicate function which determines if object b is infact the parent of object a
/// Format - parentPredicate(target, potentialParent) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns>The parent object of the target from the source list.</returns>
/// <exception cref="InvalidOperationException">Sequence contains more than one element</exception>
public static T ParentSingleOrDefault<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
return source.SingleOrDefault(q => parentPredicate(target, q));
}
/// <summary>
/// This will return all of the direct children of the current object that match the parent predicate.
/// </summary>
/// <typeparam name="T">The type of objects to enumerate.</typeparam>
/// <param name="source">The list of objects to traverse</param>
/// <param name="target">The context object in which you want to find a set of related objects from</param>
/// <param name="parentPredicate">The predicate function which determines if object a is infact the child of object b
/// Format - parentPredicate(potentialChild, target) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns></returns>
public static IEnumerable<T> Children<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
return source.Where(q => parentPredicate(q, target));
}
/// <summary>
/// In the eventuality that more than one object matches the parent predicate, this will return all objects
/// </summary>
/// <typeparam name="T">The type of objects to enumerate.</typeparam>
/// <param name="source">The list of objects to traverse</param>
/// <param name="target">The context object in which you want to find a set of related objects from</param>
/// <param name="parentPredicate">The predicate function which determines if object b is infact the parent of object a
/// Format - parentPredicate(target, potentialParent) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns>A number of parents from the source collection which match the parent predicate.</returns>
public static IEnumerable<T> Parents<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
return source.Where(q => parentPredicate(target, q));
}
/// <summary>
/// Gets all ancestors in ascending order of hierarchical distance from the target node (the first entry will be the immediate parent)
/// This function assumes that only a single parent may exist
/// </summary>
/// <typeparam name="T">The type of objects to enumerate.</typeparam>
/// <param name="source">The list of objects to traverse</param>
/// <param name="target">The context object in which you want to find a set of related objects from</param>
/// <param name="parentPredicate">The predicate function which determines if object b is infact the parent of object a
/// Format - parentPredicate(target, potentialParent) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns>A filtered subset of the source collection containing the parents the target object in ascending order.</returns>
/// <exception cref="InvalidOperationException">Sequence contains more than one element</exception>
public static IEnumerable<T> AncestorsAllAscending<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
if (target == null)
{
throw new ArgumentNullException("target");
}
List<T> results = new List<T>();
var getParentsRecursive = Functional.Y<T, IEnumerable<T>>(f =>
{
return y =>
{
//we are assuming that this item only has one parent. Should there be more, Linq will throw an exception.
T parent = ParentSingleOrDefault(source, y, parentPredicate);
if (parent != null)
{
if (!results.Contains(parent))
{
results.Add(parent);
f(parent);
}
}
return results;
};
});
return getParentsRecursive(target);
}
/// <summary>
/// Gets all ancestors in ascending order of hierarchical distance from the target node (the first entry will be the immediate parent) including itself as the first entry in the returned IEnumerable.
/// This function assumes that only a single parent may exist
/// </summary>
/// <typeparam name="T">The type of objects to enumerate.</typeparam>
/// <param name="source">The list of objects to traverse</param>
/// <param name="target">The context object in which you want to find a set of related objects from</param>
/// <param name="parentPredicate">The predicate function which determines if object b is infact the parent of object a
/// Format - parentPredicate(target, potentialParent) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns>A filtered subset of the source collection containing the parents the target object in ascending order.</returns>
/// <exception cref="InvalidOperationException">Sequence contains more than one element</exception>
public static IEnumerable<T> AncestorsAllAscendingIncludingSelf<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
var ancestorsAllAscending = source.AncestorsAllAscending(target, parentPredicate).ToList();
ancestorsAllAscending.Insert(0, target);
return ancestorsAllAscending;
}
/// <summary>
/// Get all of the descendants of the target object recursively that match the parent predicate. The collection is not sorted into any particular hierarchical order.
/// </summary>
/// <typeparam name="T">The type of the target object and source IEnumerable</typeparam>
/// <param name="source">The source IEnumerable to search against</param>
/// <param name="target">The target item or parent of the objects you wish to find.</param>
/// <param name="parentPredicate">The predicate function which determines if object b is infact the parent of object a
/// Format - parentPredicate(target, potentialParent) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns>A filtered subset of the source containing only children of the target object.</returns>
public static IEnumerable<T> DescendantsAll<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
if (target == null)
{
throw new ArgumentNullException("target");
}
var getChildrenRecursive = Functional.Y<IEnumerable<T>, IEnumerable<T>>(f =>
{
return y =>
{
List<T> ch = new List<T>();
foreach (var item in y)
{
var itemChildren = Children(source, item, parentPredicate);
ch.AddRange(itemChildren);
if (itemChildren.Count() > 0)
ch.AddRange(f(itemChildren));
}
return ch;
};
});
return getChildrenRecursive(new List<T>() { target });
}
/// <summary>
/// Gets all of the descendants recursively in order of ascending hierarchical depth from the target object or parent.
/// </summary>
/// <typeparam name="T">The type of the target object and source IEnumerable</typeparam>
/// <param name="source">The source IEnumerable to search against</param>
/// <param name="target">The target item or parent of the objects you wish to find.</param>
/// <param name="parentPredicate">The predicate function which determines if object b is infact the parent of object a
/// Format - parentPredicate(target, potentialParent) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns>A filtered subset of the source containing only children of the target object ordered into ascending hierarchical order of depth.</returns>
public static IEnumerable<T> DescendantsAllAscending<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
return source.DescendantsAllAscendingWithDepth(target, parentPredicate).Select(s => s.Key);
}
/// <summary>
/// Gets all of the descendants recursively in order of ascending hierarchical depth from the target object or parent including itself as the first entry in the returned IEnumerable.
/// </summary>
/// <typeparam name="T">The type of the target object and source IEnumerable</typeparam>
/// <param name="source">The source IEnumerable to search against</param>
/// <param name="target">The target item or parent of the objects you wish to find.</param>
/// <param name="parentPredicate">The predicate function which determines if object b is infact the parent of object a
/// Format - parentPredicate(target, potentialParent) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns>A filtered subset of the source containing the target object and the children of the target object ordered into ascending hierarchical order of depth.</returns>
public static IEnumerable<T> DescendantsAllAscendingIncludingSelf<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
List<T> decendantsAllAscending = source.DescendantsAllAscending(target, parentPredicate).ToList();
decendantsAllAscending.Insert(0, target);
return decendantsAllAscending;
}
/// <summary>
/// Gets all of the descendants recursively in order of ascending hierarchical depth from the target object or parent.
/// This is a more verbose alternative to <see cref="DescendantsAllAscending"/>, which returns a list of key value pairs in the form { (T)object, (int)depth }
/// </summary>
/// <typeparam name="T">The type of the target object and source IEnumerable.</typeparam>
/// <param name="source">The source IEnumerable to search against.</param>
/// <param name="target">The target item or parent of the objects you wish to find.</param>
/// <param name="parentPredicate">The predicate function which determines if object b is infact the parent of object a
/// Format - parentPredicate(target, potentialParent) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns>A filtered subset of the source containing only children of the target object ordered into ascending hierarchical order of depth.
/// This is a list of key value pairs in the form { (T)object, (int)depth }</returns>
public static IEnumerable<KeyValuePair<T, int>> DescendantsAllAscendingWithDepth<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
if (target == null)
{
throw new ArgumentNullException("target");
}
var getChildrenRecursive = Functional.Y<IEnumerable<KeyValuePair<T, int>>, IEnumerable<KeyValuePair<T, int>>>(f =>
{
return y =>
{
List<KeyValuePair<T, int>> ch = new List<KeyValuePair<T, int>>();
foreach (var item in y)
{
var itemChildren = Children(source, item.Key, parentPredicate).Select(s => new KeyValuePair<T, int>(s, item.Value + 1));
ch.AddRange(itemChildren);
if (itemChildren.Count() > 0)
ch.AddRange(f(itemChildren));
}
return ch;
};
});
return getChildrenRecursive(new List<KeyValuePair<T, int>>() { new KeyValuePair<T, int>(target, 0) }).OrderBy(o => o.Value);
}
/// <summary>
/// Gets all of the descendants recursively in order of ascending hierarchical depth from the target object or parent and includes itself.
/// This is a more verbose alternative to <see cref="DescendantsAllAscending"/>, which returns a list of key value pairs in the form { (T)object, (int)depth }
/// </summary>
/// <typeparam name="T">The type of the target object and source IEnumerable.</typeparam>
/// <param name="source">The source IEnumerable to search against.</param>
/// <param name="target">The target item or parent of the objects you wish to find.</param>
/// <param name="parentPredicate">The predicate function which determines if object b is infact the parent of object a
/// Format - parentPredicate(target, potentialParent) <example>(c, p) => c.ParentId == p.Id</example></param>
/// <returns>A filtered subset of the source containing only children of the target object ordered into ascending hierarchical order of depth.
/// This is a list of key value pairs in the form { (T)object, (int)depth }</returns>
public static IEnumerable<KeyValuePair<T, int>> DescendantsAllAscendingWithDepthIncludingSelf<T>(this IEnumerable<T> source, T target, Func<T, T, bool> parentPredicate) where T : class
{
var decendantsAllAscendingWithDepth = source.DescendantsAllAscendingWithDepth(target, parentPredicate).ToList();
decendantsAllAscendingWithDepth.Insert(0, new KeyValuePair<T, int>(target, 0));
return decendantsAllAscendingWithDepth;
}
}
}