Exemplo n.º 1
0
        /// <summary>
        /// Returns the sum of all cells that meet multiple criteria.
        /// </summary>
        /// <param name="arguments">The arguments used to calculate the sum.</param>
        /// <param name="context">The context for the function.</param>
        /// <returns>Returns the sum of all cells in the given range that pass the given criteria.</returns>
        public override CompileResult Execute(IEnumerable <FunctionArgument> arguments, ParsingContext context)
        {
            if (!this.ArgumentsAreValid(arguments, 3, out eErrorType errorType))
            {
                return(new CompileResult(errorType));
            }
            var sumRange = arguments.ElementAt(0).Value as ExcelDataProvider.IRangeInfo;

            if (sumRange == null)
            {
                return(new CompileResult(0d, DataType.Decimal));
            }
            var indicesOfValidCells = new List <int>();

            for (var argumentIndex = 1; argumentIndex < arguments.Count(); argumentIndex += 2)
            {
                var currentRangeToCompare = arguments.ElementAt(argumentIndex).ValueAsRangeInfo;
                if (currentRangeToCompare == null || !IfHelper.RangesAreTheSameShape(sumRange, currentRangeToCompare))
                {
                    return(new CompileResult(eErrorType.Value));
                }
                var currentCriterion = IfHelper.ExtractCriterionObject(arguments.ElementAt(argumentIndex + 1), context);

                // This will always look at every cell in the given range of cells to compare. This is done instead of
                // using the iterator provided by the range of cells to compare because the collection of cells that it iterates over
                // does not include empty cells that have not been set since the workbook's creation. This function
                // wants to consider empty cells for comparing with the criterion, but it can be better optimized.
                // A similar problem and optimization opportunity exists in the AverageIf, AverageIfs, SumIf, CountIf, and CountIfs functions.
                var passingIndices = IfHelper.GetIndicesOfCellsPassingCriterion(currentRangeToCompare, currentCriterion);
                if (argumentIndex == 1)
                {
                    indicesOfValidCells = passingIndices;
                }
                else
                {
                    indicesOfValidCells = indicesOfValidCells.Intersect(passingIndices).ToList();
                }
            }
            double sumOfValidValues = 0d;

            if (sumRange.Count() > 0)
            {
                // Again, all cells, including empty cells, need to be available here.
                // The IRangeInfo will only provide non-empty cells.
                var allSumValues = sumRange.AllValues();
                foreach (var cellIndex in indicesOfValidCells)
                {
                    var currentCellValue = allSumValues.ElementAt(cellIndex);
                    if (currentCellValue is ExcelErrorValue cellError)
                    {
                        return(new CompileResult(cellError.Type));
                    }
                    else if (ConvertUtil.IsNumeric(currentCellValue, true))
                    {
                        sumOfValidValues += ConvertUtil.GetValueDouble(currentCellValue);
                    }
                }
            }
            return(this.CreateResult(sumOfValidValues, DataType.Decimal));
        }
Exemplo n.º 2
0
        /// <summary>
        /// This function applies criteria to cells across multiple ranges and counts the number of times
        /// all criteria are met. If multiple cell ranges are being compared against criteria, all ranges must
        /// have the same number of rows and columns as the first given range, but the ranges do not have to be
        /// adjacent to each other.
        /// </summary>
        /// <param name="arguments">The arguments being evaluated.</param>
        /// <param name="context">The context for this function.</param>
        /// <returns>Returns the number of times all criteria are met across a row of cells.</returns>
        public override CompileResult Execute(IEnumerable <FunctionArgument> arguments, ParsingContext context)
        {
            if (this.ArgumentCountIsValid(arguments, 2) == false)
            {
                return(new CompileResult(eErrorType.Value));
            }
            var firstRangeToCompare = arguments.ElementAt(0).ValueAsRangeInfo;

            if (firstRangeToCompare == null)
            {
                return(new CompileResult(eErrorType.Value));
            }
            var indicesOfValidCells = new List <int>();

            for (var argumentIndex = 0; argumentIndex < arguments.Count(); argumentIndex += 2)
            {
                var currentRangeToCompare = arguments.ElementAt(argumentIndex).ValueAsRangeInfo;
                if (currentRangeToCompare == null || !IfHelper.RangesAreTheSameShape(firstRangeToCompare, currentRangeToCompare))
                {
                    return(new CompileResult(eErrorType.Value));
                }
                var currentCriterion = IfHelper.ExtractCriterionObject(arguments.ElementAt(argumentIndex + 1), context);

                // This will always look at every cell in the given range of cells to compare. This is done instead of
                // using the iterator provided by the range of cells to compare because the collection of cells that it iterates over
                // does not include empty cells that have not been set since the workbook's creation. This function
                // wants to consider empty cells for comparing with the criterion, but it can be better optimized.
                // A similar problem and optimization opportunity exists in the AverageIf, AverageIfs, SumIf, SumIfs, and CountIf functions.
                var passingIndices = IfHelper.GetIndicesOfCellsPassingCriterion(currentRangeToCompare, currentCriterion);
                if (argumentIndex == 0)
                {
                    indicesOfValidCells = passingIndices;
                }
                else
                {
                    indicesOfValidCells = indicesOfValidCells.Intersect(passingIndices).ToList();
                }
            }
            double count = indicesOfValidCells.Count();

            return(this.CreateResult(count, DataType.Integer));
        }