/// <summary>
        ///     Creates a new table from a time aligned source table
        ///     Due to the dynamic implementation and with big data performance hits are possible
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="newSerieKeyOrName">Name of zipped serie in the new table</param>
        /// <param name="zipFunc">
        ///     Given is a dynamic source table where you can get the current iteration value
        ///     e.g. you have to series with the key "A" and "B" in the source table.
        ///     In this case a lambda expression like 't => t.A + t.B' would add them
        ///     serie A has {1,2,3,4}
        ///     serie B has {2,2,2,4}
        ///     zipped serie has {3,4,5,8}
        /// </param>
        /// <returns>New table with the zipped result</returns>
        public INullableQueryTable <T> ZipToNew(string newSerieKeyOrName, Func <dynamic, T?> zipFunc)
        {
            var table        = new NullableQueryTable <T>();
            var sourceTables = GroupSeries();
            var resultTables = new List <INullableQueryTable <T> >();

            foreach (var sourceTable in sourceTables)
            {
                var dynamicTable = new DynamicTableValues(sourceTable);
                var firstSerie   = sourceTable.Series.First();
                var count        = firstSerie.Rows.Count;
                var newRows      = new List <ISingleDataRow <T?> >(count);
                var newSerie     = new NullableQuerySerie <T>(newRows, firstSerie).Alias(newSerieKeyOrName);
                table.AddSerie(newSerie);
                for (var i = 0; i < count; i++)
                {
                    dynamicTable.Index = i;
                    var newValue = zipFunc(dynamicTable);
                    newRows.Add(new SingleDataRow <T?>(firstSerie.Rows[i].TimeUtc, newValue));
                }

                resultTables.Add(table);
            }
            return(resultTables.MergeTables());
        }
        public INullableQuerySerie <T> Clone(string serieName)
        {
            var serie = new NullableQuerySerie <T>(Rows, this)
            {
                Name = serieName
            };

            return(serie);
        }
        public INullableQuerySerie <T> CalcValue(Func <T?, T?> calculationFunc, string newSerieName = null)
        {
            var rows = new List <ISingleDataRow <T?> >(Rows.Count);

            if (Rows.Any())
            {
                rows.AddRange(Rows.Select(row => new SingleDataRow <T?>(row.TimeUtc, calculationFunc(row.Value))));
            }
            var newSerie = new NullableQuerySerie <T>(rows, this);

            if (newSerieName != null)
            {
                newSerie.Name = newSerieName;
            }
            return(newSerie);
        }
        public override IObjectQuerySerieBase GetOrCreateSerie(string name)
        {
            var serie = TryGetSerie(name);

            if (serie == null)
            {
                var firstSerie = Series.Values.FirstOrDefault();
                if (firstSerie != null)
                {
                    var newSerie = new NullableQuerySerie <T>(new List <ISingleDataRow <T?> >(firstSerie.Rows.Select(i => new SingleDataRow <T?>(i.Key, null))), firstSerie.StartTime, firstSerie.EndTime).Alias(name);
                    AddSerie(newSerie);
                    serie = newSerie;
                }
            }
            return(serie);
        }
        public INullableQuerySerie <T> Zip(INullableQuerySerie <T> secondQuery, string resultQueryName,
                                           Func <T?, T?, T?> transformAction)
        {
            if (Rows.Count != secondQuery.Rows.Count)
            {
                throw new ArgumentOutOfRangeException(nameof(secondQuery),
                                                      "Zip with different length of row not possible");
            }
            var resultRows = new List <ISingleDataRow <T?> >(secondQuery.Rows.Count);

            var result = new NullableQuerySerie <T>(resultRows, this).Alias(resultQueryName);

            for (int i = 0; i < Rows.Count; i++)
            {
                if (Rows[i].TimeUtc != secondQuery.Rows[i].TimeUtc)
                {
                    throw new ArgumentOutOfRangeException(nameof(secondQuery), "Zip with not aligned times");
                }

                resultRows.Add(new SingleDataRow <T?>(Rows[i].TimeUtc,
                                                      transformAction(Rows[i].Value, secondQuery.Rows[i].Value)));
            }
            return(result);
        }