BO4E.Extensions/COM/VerbrauchExtension.cs
/// <summary>
///     De-tangles a list of overlapping Verbrauch entries where entries can be subsets of other entries.
/// </summary>
/// <example>
///     [---v1---)
///     [-----v2------)
///     [---------v3---------)
///     is transformed into
///     [--v1---)
///     ........[-v2--)
///     ..............[--v3--)
/// </example>
/// <param name="input"></param>
/// <returns></returns>
De-tangles a list of overlapping Verbrauch entries where entries can be subsets of other entries.
De - tangles a list of overlapping Verbrauch entries where entries can be subsets of other entries .
public static List<Verbrauch> Detangle(IEnumerable<Verbrauch> input)
        {
            var resultSet = new HashSet<Verbrauch>();
            var groups = input.OrderBy(v => (v.Startdatum, v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit))
                .GroupBy(v => new Tuple<Wertermittlungsverfahren?, string, Mengeneinheit>
                (
                    v.Wertermittlungsverfahren,
                    v.Obiskennzahl,
                    v.Einheit
                ));
            foreach (var vGroup in groups)
            {
                var subResult = new HashSet<Verbrauch>();
                var adjacentVerbrauchs =
                    from x in vGroup join y in vGroup on x.Enddatum equals y.Startdatum select new { x, y };
                foreach (var av in adjacentVerbrauchs)
                {
                    subResult.Add(av.x);
                    subResult.Add(av.y);
                }
                var fullyRedundantVerbrauchs = from av in adjacentVerbrauchs
                                               join z in vGroup on new { av.x.Startdatum, av.y.Enddatum } equals new { z.Startdatum, z.Enddatum }
                                               select new { av, z };
                foreach (var frv in fullyRedundantVerbrauchs)
                {
                    if (frv.av.x.Wert + frv.av.y.Wert != frv.z.Wert)
                        throw new ArgumentException(
                            $"Inconsistent data detected: {JsonConvert.SerializeObject(frv.av.x)} + {JsonConvert.SerializeObject(frv.av.y)} ≠ {JsonConvert.SerializeObject(frv.z)}");
                    subResult.Remove(frv.z);
                }
                subResult.UnionWith(vGroup);
                foreach (var z in new HashSet<Verbrauch>(subResult))
                {
                    var ys = subResult.Where(y => z.Contains(y) && !z.Equals(y));
                    if (ys.Any())
                    {
                        var tps = new TimePeriodSubtractor<TimeRange>();
                        var source = new TimePeriodCollection { z.GetTimeRange() };
                        var subtract = new TimePeriodCollection();
                        subtract.AddAll(ys.Select(y => y.GetTimeRange()));
                        var subtractionResult = tps.SubtractPeriods(source, subtract);
                        var xs = new HashSet<Verbrauch>();
                        foreach (var tr in subtractionResult)
                        {
                            var v = new Verbrauch
                            {
                                Einheit = z.Einheit,
                                Wertermittlungsverfahren = z.Wertermittlungsverfahren,
                                Obiskennzahl = z.Obiskennzahl,
                                Startdatum = new DateTimeOffset(tr.Start, TimeSpan.Zero),
                                Enddatum = new DateTimeOffset(tr.End, TimeSpan.Zero)
                            };
                            xs.Add(v);
                        }
                        var totalXWert = z.Wert - ys.Select(y => y.Wert).Sum();
                        var totalXDuration = xs.Select(x => x.GetDuration().TotalSeconds).Sum();
                        foreach (var x in xs)
                            x.Wert = totalXWert * (decimal)x.GetDuration().TotalSeconds / (decimal)totalXDuration;
                        subResult.Remove(z);
                        subResult.UnionWith(xs);
                    }
                }
                resultSet.UnionWith(subResult);
            }
            var result = new List<Verbrauch>(resultSet);
            result.Sort(new VerbrauchDateTimeComparer());
            return result;
        }
public static List < Verbrauch > Detangle ( IEnumerable < Verbrauch > input ) { var resultSet = new HashSet < Verbrauch > ( ) ; var groups = input . OrderBy ( v => ( v . Startdatum , v . Wertermittlungsverfahren , v . Obiskennzahl , v . Einheit ) ) . GroupBy ( v => new Tuple < Wertermittlungsverfahren ? , string , Mengeneinheit > ( v . Wertermittlungsverfahren , v . Obiskennzahl , v . Einheit ) ) ; foreach ( var vGroup in groups ) { var subResult = new HashSet < Verbrauch > ( ) ; var adjacentVerbrauchs = from x in vGroup join y in vGroup on x . Enddatum equals y . Startdatum select new { x , y } ; foreach ( var av in adjacentVerbrauchs ) { subResult . Add ( av . x ) ; subResult . Add ( av . y ) ; } var fullyRedundantVerbrauchs = from av in adjacentVerbrauchs join z in vGroup on new { av . x . Startdatum , av . y . Enddatum } equals new { z . Startdatum , z . Enddatum } select new { av , z } ; foreach ( var frv in fullyRedundantVerbrauchs ) { if ( frv . av . x . Wert + frv . av . y . Wert != frv . z . Wert ) throw new ArgumentException ( $" Inconsistent data detected:  { JsonConvert . SerializeObject ( frv . av . x ) }  +  { JsonConvert . SerializeObject ( frv . av . y ) }  ≠ {J s onConvert.S e rializeObject(f r v.z ) } " ) ; subResult . Remove ( frv . z ) ; } subResult . UnionWith ( vGroup ) ; foreach ( var z in new HashSet < Verbrauch > ( subResult ) ) { var ys = subResult . Where ( y => z . Contains ( y ) && ! z . Equals ( y ) ) ; if ( ys . Any ( ) ) { var tps = new TimePeriodSubtractor < TimeRange > ( ) ; var source = new TimePeriodCollection { z . GetTimeRange ( ) } ; var subtract = new TimePeriodCollection ( ) ; subtract . AddAll ( ys . Select ( y => y . GetTimeRange ( ) ) ) ; var subtractionResult = tps . SubtractPeriods ( source , subtract ) ; var xs = new HashSet < Verbrauch > ( ) ; foreach ( var tr in subtractionResult ) { var v = new Verbrauch { Einheit = z . Einheit , Wertermittlungsverfahren = z . Wertermittlungsverfahren , Obiskennzahl = z . Obiskennzahl , Startdatum = new DateTimeOffset ( tr . Start , TimeSpan . Zero ) , Enddatum = new DateTimeOffset ( tr . End , TimeSpan . Zero ) } ; xs . Add ( v ) ; } var totalXWert = z . Wert - ys . Select ( y => y . Wert ) . Sum ( ) ; var totalXDuration = xs . Select ( x => x . GetDuration ( ) . TotalSeconds ) . Sum ( ) ; foreach ( var x in xs ) x . Wert = totalXWert * ( decimal ) x . GetDuration ( ) . TotalSeconds / ( decimal ) totalXDuration ; subResult . Remove ( z ) ; subResult . UnionWith ( xs ) ; } } resultSet . UnionWith ( subResult ) ; } var result = new List < Verbrauch > ( resultSet ) ; result . Sort ( new VerbrauchDateTimeComparer ( ) ) ; return result ; }
De-tangles a list of overlapping Verbrauch entries where entries can be subsets of other entries.
De - tangles a list of overlapping Verbrauch entries where entries can be subsets of other entries .
//var filteredInput = KeepShortestSlices(input); // find pairs (x,y) where x.end == y.start: // |----x----|--------y--------| //var adjacentVerbrauchs = from x in vGroup join y in vGroup on x.enddatum equals y.startdatum select new { x, y }; // |----x----|--------y--------| // |-------------z-------------| // ==> delete z from result where z.start == x.start and z.end == y.end //var fullyRedundantVerbrauchs = from av in adjacentVerbrauchs join z in vGroup on new { av.x.startdatum, av.y.enddatum } equals new { z.startdatum, z.enddatum } select new { av, z }; // |----------x----------|---y---| // |---------------z-------------| // or // |---y---|----------x----------| // |---------------z-------------| // or // |---x1--|-----y------|---x2---| // |--------------z--------------| // y and z are given. find x such that x.value == y.value+z.value //subResult.UnionWith(vGroup);
None
input
IEnumerable<Verbrauch>
None
example
None