これはあなたが求めているものですか?
IEnumerable<record> ParentGetSomeRecords()
{
foreach(var item in someItems)
foreach(var record in GetSomeRecords())
yield return record;
}
前述のように、これは単一レベルの子に対してのみ機能しますが、サンプルコードと最も同等です。
アップデート
一部の人々は、階層構造をフラット化する機能が必要だと信じているようです。幅優先探索を実行する拡張メソッドは次のとおりです(子の前に兄弟を取得します)。
単一のアイテムから来る:
[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this T source, Func<T, IEnumerable<T>> selector)
{
Contract.Requires(!ReferenceEquals(source, null));
Contract.Requires(selector != null);
Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
var pendingChildren = new List<T> {source};
while (pendingChildren.Any())
{
var localPending = pendingChildren.ToList();
pendingChildren.Clear();
foreach (var child in localPending)
{
yield return child;
var results = selector(child);
if (results != null)
pendingChildren.AddRange(results);
}
}
}
これは次のように使用できます。
record rec = ...;
IEnumerable<record> flattened = rec.BreadthFirstFlatten(r => r.ChildRecords);
これにより、すべてのrecの子、すべての子の子などがIEnumerable<record>
含まれるようになります。rec
のコレクションから来ている場合はrecords
、次のコードを使用してください。
[Pure]
private static IEnumerable<T> BreadthFirstFlatten<T, TResult>(IEnumerable<T> source, Func<T, TResult> selector, Action<ICollection<T>, TResult> addMethod)
{
Contract.Requires(source != null);
Contract.Requires(selector != null);
Contract.Requires(addMethod != null);
Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
var pendingChildren = new List<T>(source);
while (pendingChildren.Any())
{
var localPending = pendingChildren.ToList();
pendingChildren.Clear();
foreach (var child in localPending)
{
yield return child;
var results = selector(child);
if (!ReferenceEquals(results, null))
addMethod(pendingChildren, results);
}
}
}
[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
Contract.Requires(source != null);
Contract.Requires(selector != null);
Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
return BreadthFirstFlatten(source, selector, (collection, arg2) => collection.AddRange(arg2));
}
[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this IEnumerable<T> source, Func<T, T> selector)
{
Contract.Requires(source != null);
Contract.Requires(selector != null);
Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
return BreadthFirstFlatten(source, selector, (collection, arg2) => collection.Add(arg2));
}
これらの2つの拡張方法は、次のように使用できます。
IEnumerable<records> records = ...;
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ChildRecords);
または逆方向から:
IEnumerable<record> records = ...;
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ParentRecords);
これらの拡張メソッドはすべて反復的であるため、スタックサイズによる制限はありません。
事前注文と事後注文の深さ優先探索を含む、これらのタイプのメソッドがたくさんあります。それらを表示したい場合は、リポジトリを作成してどこかにアップロードします:)