Add tree traversal functions. Skip self in Folder/User descendants
This commit is contained in:
parent
e3e03fda8b
commit
e136767b12
|
@ -8,12 +8,14 @@ public static class CredentialsMethods
|
|||
{
|
||||
public static Session? Login(this Credentials credentials)
|
||||
{
|
||||
if (credentials.Username.IsNullOrEmpty() || credentials.Password.IsNullOrEmpty())
|
||||
var (username, password) = credentials;
|
||||
|
||||
if (username.IsNullOrEmpty() || password.IsNullOrEmpty())
|
||||
return null;
|
||||
|
||||
var user = Db.GetUserByEmail(credentials.Username);
|
||||
var user = Db.GetUserByEmail(username);
|
||||
|
||||
if (user is null || !user.VerifyPassword(credentials.Password))
|
||||
if (user is null || !user.VerifyPassword(password))
|
||||
return null;
|
||||
|
||||
var session = new Session(user);
|
||||
|
|
|
@ -21,7 +21,9 @@ public static class FolderMethods
|
|||
|
||||
public static IEnumerable<Folder> DescendantFolders(this Folder parent)
|
||||
{
|
||||
return parent.Traverse(ChildFolders);
|
||||
return parent
|
||||
.TraverseDepthFirstPreOrder(ChildFolders)
|
||||
.Skip(1); // skip self
|
||||
}
|
||||
|
||||
public static Boolean IsDescendantOf(this Folder folder, Folder ancestor)
|
||||
|
|
|
@ -73,7 +73,9 @@ public static class UserMethods
|
|||
|
||||
public static IEnumerable<User> DescendantUsers(this User parent)
|
||||
{
|
||||
return parent.Traverse(ChildUsers);
|
||||
return parent
|
||||
.TraverseDepthFirstPreOrder(ChildUsers)
|
||||
.Skip(1); // skip self
|
||||
}
|
||||
|
||||
public static Boolean IsDescendantOf(this User user, User ancestor)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System.Reactive.Linq;
|
||||
using InnovEnergy.App.Backend.Database;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
|
@ -8,7 +7,6 @@ public static class Program
|
|||
{
|
||||
public static void Main(String[] args)
|
||||
{
|
||||
|
||||
Db.CreateFakeRelations();
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
namespace InnovEnergy.Lib.Utils;
|
||||
|
||||
public static class TreeTraversal
|
||||
{
|
||||
// public static IEnumerable<T> TraverseDepthFirstPreOrder<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||
// {
|
||||
// var stack = new Stack<IEnumerator<T>>();
|
||||
//
|
||||
// var iterator = root.AsSingleEnumerator();
|
||||
//
|
||||
// while (true)
|
||||
// {
|
||||
// while (iterator.MoveNext())
|
||||
// {
|
||||
// yield return iterator.Current;
|
||||
//
|
||||
// iterator = getChildren(iterator.Current).GetEnumerator();
|
||||
// stack.Push(iterator);
|
||||
// }
|
||||
//
|
||||
// iterator.Dispose();
|
||||
// if (stack.Count == 0)
|
||||
// yield break;
|
||||
//
|
||||
// iterator = stack.Pop();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
public static IEnumerable<T> TraverseDepthFirstPreOrder<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
return Traverse(root,
|
||||
getChildren,
|
||||
branchOpen: true,
|
||||
branchClose: false,
|
||||
leaf: true);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> TraverseDepthFirstPostOrder<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
return Traverse(root,
|
||||
getChildren,
|
||||
branchOpen: false,
|
||||
branchClose: true,
|
||||
leaf: true);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> TraverseLeaves<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
return Traverse(root,
|
||||
getChildren,
|
||||
branchOpen: false,
|
||||
branchClose: true,
|
||||
leaf: false);
|
||||
}
|
||||
|
||||
|
||||
private static IEnumerable<T> Traverse<T>(T root,
|
||||
Func<T, IEnumerable<T>> getChildren,
|
||||
Boolean branchOpen,
|
||||
Boolean branchClose,
|
||||
Boolean leaf)
|
||||
{
|
||||
// the if-checks on the constant booleans are
|
||||
// almost free because of modern branch predictors
|
||||
|
||||
var stack = new Stack<IEnumerator<T>>();
|
||||
var it = root.AsSingleEnumerator();
|
||||
it.MoveNext();
|
||||
|
||||
while (true)
|
||||
{
|
||||
//////// going down ////////
|
||||
|
||||
while (true)
|
||||
{
|
||||
var cit = getChildren(it.Current).GetEnumerator();
|
||||
|
||||
if (cit.MoveNext()) // node has children, must be a branch
|
||||
{
|
||||
if (branchOpen)
|
||||
yield return it.Current;
|
||||
|
||||
stack.Push(it);
|
||||
it = cit;
|
||||
}
|
||||
else // no children, hence a leaf
|
||||
{
|
||||
var node = it.Current;
|
||||
|
||||
if (leaf)
|
||||
yield return node;
|
||||
|
||||
if (!it.MoveNext())
|
||||
break; // no more siblings: goto parent
|
||||
}
|
||||
}
|
||||
|
||||
//////// going up ////////
|
||||
|
||||
while (true)
|
||||
{
|
||||
it.Dispose();
|
||||
if (stack.Count == 0) yield break; // we got to the bottom of the stack, were done
|
||||
|
||||
it = stack.Pop();
|
||||
|
||||
var node = it.Current;
|
||||
|
||||
if (branchClose)
|
||||
yield return node; // we've seen all its children: close the branch
|
||||
|
||||
if (it.MoveNext())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<T> TraverseBreadthFirst<T>(this T node, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
var queue = new Queue<IEnumerator<T>>();
|
||||
|
||||
var iterator = node.AsSingleEnumerator();
|
||||
|
||||
while(true)
|
||||
{
|
||||
while (iterator.MoveNext())
|
||||
{
|
||||
yield return iterator.Current;
|
||||
|
||||
iterator.Current
|
||||
.Apply(getChildren)
|
||||
.GetEnumerator()
|
||||
.Apply(queue.Enqueue);
|
||||
}
|
||||
|
||||
iterator.Dispose();
|
||||
|
||||
if (queue.Count == 0)
|
||||
yield break;
|
||||
|
||||
iterator = queue.Dequeue();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static System.Runtime.CompilerServices.MethodImplOptions;
|
||||
|
||||
|
@ -6,6 +7,7 @@ namespace InnovEnergy.Lib.Utils;
|
|||
|
||||
public static class Utils
|
||||
{
|
||||
public static Boolean IsNull<T>([NotNullWhen(returnValue: false)] this T t) => t is null;
|
||||
|
||||
public static IEnumerable<String> GetEnumStrings<T>(this T e) where T : Enum
|
||||
{
|
||||
|
@ -20,7 +22,7 @@ public static class Utils
|
|||
[DebuggerStepThrough][MethodImpl(AggressiveInlining)]
|
||||
public static T ConvertTo<T>(this IConvertible c) where T : IConvertible
|
||||
{
|
||||
var t = typeof (T);
|
||||
var t = typeof(T);
|
||||
|
||||
var type = t.IsEnum
|
||||
? Enum.GetUnderlyingType(t)
|
||||
|
@ -50,13 +52,13 @@ public static class Utils
|
|||
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||
public static R Apply<T, R>(this T t, Func<T, R> f) => f(t);
|
||||
|
||||
[DebuggerStepThrough]
|
||||
[MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||
|
||||
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||
public static R Apply<T1, T2, R>(this (T1 p1, T2 p2) t, Func<T1, T2, R> f) => f(t.p1, t.p2);
|
||||
|
||||
[DebuggerStepThrough]
|
||||
[MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||
public static R Apply<T1, T2, T3, R>(this (T1 p1, T2 p2, T3 p3) t, Func<T1, T2, T3, R> f) => f(t.p1, t.p2, t.p3);
|
||||
|
||||
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||
public static R Apply<T1, T2, T3, R>(this (T1 p1, T2 p2, T3 p3) t, Func<T1, T2, T3, R> f) => f(t.p1, t.p2, t.p3);
|
||||
|
||||
[DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)]
|
||||
public static R ApplyOrDefault<T, R>(this T t, Func<T, R> f, R @default)
|
||||
|
@ -79,7 +81,7 @@ public static class Utils
|
|||
? res
|
||||
: res + length;
|
||||
}
|
||||
|
||||
|
||||
public static Decimal Modulo(this Decimal dividend, Decimal divisor)
|
||||
{
|
||||
var res = dividend % divisor;
|
||||
|
@ -89,111 +91,39 @@ public static class Utils
|
|||
: res + divisor;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Traverse<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
var stack = new Stack<IEnumerator<T>>();
|
||||
var it = root.AsSingleEnumerator();
|
||||
it.MoveNext();
|
||||
|
||||
while (true)
|
||||
{
|
||||
//////// going down ////////
|
||||
|
||||
while (true)
|
||||
{
|
||||
var cit = getChildren(it.Current).GetEnumerator();
|
||||
|
||||
if (cit.MoveNext()) // node has children, must be a branch
|
||||
{
|
||||
yield return it.Current;
|
||||
|
||||
stack.Push(it);
|
||||
it = cit;
|
||||
}
|
||||
else // no children, hence a leaf
|
||||
{
|
||||
var node = it.Current;
|
||||
|
||||
yield return node;
|
||||
|
||||
if (!it.MoveNext())
|
||||
break; // no more siblings: goto parent
|
||||
}
|
||||
}
|
||||
|
||||
//////// going up ////////
|
||||
|
||||
while (true)
|
||||
{
|
||||
it.Dispose();
|
||||
if (stack.Count == 0)
|
||||
yield break; // we got to the bottom of the stack, were done
|
||||
|
||||
it = stack.Pop();
|
||||
|
||||
if (it.MoveNext())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Int32 Clamp(this Int32 value, Int32 minValue, Int32 maxValue)
|
||||
{
|
||||
var clamped = Math.Min(maxValue, value);
|
||||
clamped = Math.Max(minValue, clamped);
|
||||
|
||||
|
||||
return clamped;
|
||||
}
|
||||
|
||||
|
||||
public static Double Clamp(this Double value, Double minValue, Double maxValue)
|
||||
{
|
||||
var clamped = Math.Min(maxValue, value);
|
||||
clamped = Math.Max(minValue, clamped);
|
||||
|
||||
|
||||
return clamped;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static Decimal Clamp(this Decimal value, Decimal minValue, Decimal maxValue)
|
||||
{
|
||||
var clamped = Math.Min(maxValue, value);
|
||||
clamped = Math.Max(minValue, clamped);
|
||||
|
||||
|
||||
return clamped;
|
||||
}
|
||||
|
||||
|
||||
#pragma warning disable 8714
|
||||
|
||||
public static async Task<R> ApplyOrDefault<T, R>(this T t, Func<T, Task<R>> f, R @default)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await f(t);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return @default;
|
||||
}
|
||||
}
|
||||
|
||||
public static R ValueOrDefault<T, R>(this Dictionary<T, R> dict, T key, R defaultValue)
|
||||
public static R ValueOrDefault<T, R>(this Dictionary<T, R> dict, T key, R defaultValue) where T : notnull
|
||||
{
|
||||
return dict.TryGetValue(key, out var value)
|
||||
? value
|
||||
: defaultValue;
|
||||
? value
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
|
||||
public static Dictionary<K, V> CombineDicts<K,V>(params IEnumerable<KeyValuePair<K,V>>[] dicts)
|
||||
{
|
||||
return dicts.Flatten().ToDictionary(kv => kv.Key, kv => kv.Value);
|
||||
}
|
||||
|
||||
#pragma warning restore 8714
|
||||
|
||||
|
||||
public static void CopyFilesRecursively(String source, String target)
|
||||
{
|
||||
CopyFilesRecursively(new DirectoryInfo(source), new DirectoryInfo(target));
|
||||
|
@ -210,27 +140,4 @@ public static class Utils
|
|||
|
||||
|
||||
public static String ExecutingProcessName => Process.GetCurrentProcess().ProcessName;
|
||||
|
||||
public static IEnumerable<IEnumerable<T>> TraverseWithPath<T>(this T root, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
var stack = new Stack<IEnumerator<T>>();
|
||||
stack.Push(root.AsSingleEnumerator());
|
||||
|
||||
do
|
||||
{
|
||||
for (var top = stack.Peek(); top.MoveNext(); top = Push(top))
|
||||
yield return stack.Select(p => p.Current);
|
||||
|
||||
var popped = stack.Pop();
|
||||
popped.Dispose();
|
||||
}
|
||||
while (stack.Count > 0);
|
||||
|
||||
IEnumerator<T> Push(IEnumerator<T> node)
|
||||
{
|
||||
var top = getChildren(node.Current).GetEnumerator();
|
||||
stack.Push(top);
|
||||
return top;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue