| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="GroupingEngine.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System; |
| | | 8 | | using System.Collections.Generic; |
| | | 9 | | using System.Linq; |
| | | 10 | | using System.Reactive.Subjects; |
| | | 11 | | |
| | | 12 | | namespace MyNet.Observable.Collections.Grouping; |
| | | 13 | | |
| | | 14 | | /// <summary> |
| | | 15 | | /// Manages the grouping configuration for a reactive collection pipeline. |
| | | 16 | | /// Exposes an observable that emits the current grouping properties whenever they change, |
| | | 17 | | /// allowing downstream consumers (e.g. <see cref="MyNet.Observable.Collections.ExtendedCollection{T}"/>) |
| | | 18 | | /// to reactively recompute grouped representations. |
| | | 19 | | /// </summary> |
| | | 20 | | /// <typeparam name="T">The type of items to be grouped.</typeparam> |
| | | 21 | | public sealed class GroupingEngine<T> : IDisposable |
| | | 22 | | where T : notnull |
| | | 23 | | { |
| | 192 | 24 | | private readonly BehaviorSubject<IGroupingProperty<T>[]> _subject = new([]); |
| | | 25 | | |
| | | 26 | | /// <summary> |
| | | 27 | | /// Computes a flat list of groups from a snapshot of items and a set of grouping properties. |
| | | 28 | | /// Groups are identified by a composite key built by concatenating the values of all grouping properties. |
| | | 29 | | /// </summary> |
| | | 30 | | /// <param name="items">The items to group.</param> |
| | | 31 | | /// <param name="groupingProperties">The active grouping properties.</param> |
| | | 32 | | /// <returns>A read-only list of <see cref="CollectionGroup{T}"/>. Returns an empty list if no grouping properties a |
| | | 33 | | public static IReadOnlyList<CollectionGroup<T>> ComputeGroups(IReadOnlyList<T> items, IGroupingProperty<T>[] groupin |
| | | 34 | | { |
| | 108 | 35 | | if (groupingProperties.Length == 0) |
| | 96 | 36 | | return []; |
| | | 37 | | |
| | 12 | 38 | | var keySelectors = groupingProperties |
| | 12 | 39 | | .Select(g => g.ProvideExpression().Compile()) |
| | 12 | 40 | | .ToArray(); |
| | | 41 | | |
| | 12 | 42 | | return |
| | 12 | 43 | | [ |
| | 12 | 44 | | .. items |
| | 12 | 45 | | .GroupBy(buildKey) |
| | 12 | 46 | | .Select(g => new CollectionGroup<T>(g.Key, [.. g])) |
| | 12 | 47 | | ]; |
| | | 48 | | |
| | | 49 | | string buildKey(T item) => string.Join("|", keySelectors.Select(s => s(item)?.ToString() ?? "<null>")); |
| | | 50 | | } |
| | | 51 | | |
| | | 52 | | /// <summary> |
| | | 53 | | /// Gets the currently active grouping properties. |
| | | 54 | | /// </summary> |
| | 192 | 55 | | public IGroupingProperty<T>[] Current { get; private set; } = []; |
| | | 56 | | |
| | | 57 | | /// <summary> |
| | | 58 | | /// Gets an observable that emits the current grouping properties whenever they change. |
| | | 59 | | /// </summary> |
| | 192 | 60 | | public IObservable<IGroupingProperty<T>[]> Grouping => _subject; |
| | | 61 | | |
| | | 62 | | /// <summary> |
| | | 63 | | /// Sets the active grouping properties and notifies subscribers. |
| | | 64 | | /// </summary> |
| | | 65 | | /// <param name="grouping">The new set of grouping properties to apply.</param> |
| | | 66 | | public void Set(IEnumerable<IGroupingProperty<T>> grouping) |
| | | 67 | | { |
| | 12 | 68 | | Current = [.. grouping]; |
| | 12 | 69 | | _subject.OnNext(Current); |
| | 12 | 70 | | } |
| | | 71 | | |
| | | 72 | | /// <summary> |
| | | 73 | | /// Clears all active grouping properties and notifies subscribers. |
| | | 74 | | /// </summary> |
| | | 75 | | public void Clear() |
| | | 76 | | { |
| | 81 | 77 | | Current = []; |
| | 81 | 78 | | _subject.OnNext(Current); |
| | 81 | 79 | | } |
| | | 80 | | |
| | | 81 | | /// <inheritdoc/> |
| | 162 | 82 | | public void Dispose() => _subject.Dispose(); |
| | | 83 | | } |
| | | 84 | | |