/// <summary>
        /// Returns the element that is closest the the average of the values returned by the selector function.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements of the source sequence.</typeparam>
        /// <typeparam name="TSelector">The type of the element returned by the selector of the source sequence. TSelector must be numeric.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="selector">A function that return a numeric value used to evaluate the average sequence value.</param>
        /// <param name="matchType">The type of matching to apply when seeking the closest item.</param>
        /// <returns>The element that is closest the the average of the values returned by the selector function.</returns>
        /// <remarks>Closest is defined by the match type.</remarks>
        /// <exception cref="System.ArgumentNullException">source is null.</exception>
        /// <exception cref="System.ArgumentNullException">selector is null.</exception>
        /// <exception cref="System.ArgumentException">source must have one or more elements.</exception>
        /// <exception cref="System.InvalidOperationException">Selector must return a numeric value.</exception>
        public static TSource ElementAtAverage <TSource, TSelector>(this IEnumerable <TSource> source, Func <TSource, TSelector> selector, AverageMatchType matchType)
        {
            Helper.InvalidateNullParam(source, "source");
            Helper.InvalidateNullParam(selector, "selector");
            Helper.InvalidateEmptySequence(source, "source");
            Helper.InvalidateNonNumeric <TSelector>("ElementAtAverage");

            TSelector tempSum = default(TSelector);
            int       count   = 0;

            foreach (TSource item in source)
            {
                if (count == 0)
                {
                    tempSum = selector(item);
                    count   = 1;
                    continue;
                }

                tempSum = Arithmetic.Helper <TSelector> .Add(tempSum, selector(item));

                count++;
            }

            decimal ts      = Convert.ToDecimal(tempSum);
            decimal tempAvg = ts / count;

            TSource minElement = default(TSource);
            TSource maxElement = default(TSource);
            decimal minValue   = 0;
            decimal maxValue   = 0;

            bool firstMin = true;
            bool firstMax = true;

            foreach (TSource item in source)
            {
                decimal temp = Convert.ToDecimal(selector(item));

                if (temp.CompareTo(tempAvg) <= 0)
                {
                    if (firstMin || minValue.CompareTo(temp) <= 0)
                    {
                        firstMin   = false;
                        minValue   = temp;
                        minElement = item;
                    }
                }

                if (temp.CompareTo(tempAvg) >= 0)
                {
                    if (firstMax || maxValue.CompareTo(temp) >= 0)
                    {
                        firstMax   = false;
                        maxValue   = temp;
                        maxElement = item;
                    }
                }
            }

            decimal distanceOfMaxValue = maxValue - tempAvg;
            decimal distanceOfMinValue = tempAvg - minValue;

            switch (matchType)
            {
            case AverageMatchType.Exact:
                if (distanceOfMaxValue == 0)
                {
                    return(maxElement);
                }
                else
                {
                    return(default(TSource));
                }

            case AverageMatchType.Closest:
                if (distanceOfMaxValue < distanceOfMinValue)
                {
                    return(maxElement);
                }
                else
                {
                    return(minElement);
                }

            case AverageMatchType.ExactOrLarger:
                return(maxElement);

            case AverageMatchType.ExactOrSmaller:
                return(minElement);

            default:
                throw new ArgumentOutOfRangeException("matchType", string.Format(Resources.exceptionMatchType, matchType));
            }
        }
Example #2
0
        /// <summary>
        /// Returns the element that is closest the the average of the values returned by the selector function.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements of the source sequence.</typeparam>
        /// <typeparam name="TSelector">The type of the element returned by the selector of the source sequence. TSelector must be numeric.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="selector">A function that return a numeric value used to evaluate the average sequence value.</param>
        /// <param name="matchType">The type of matching to apply when seeking the closest item.</param>
        /// <returns>The element that is closest the the average of the values returned by the selector function.</returns>
        /// <remarks>Closest is defined by the match type.</remarks>
        /// <exception cref="System.ArgumentNullException">source is null.</exception>
        /// <exception cref="System.ArgumentNullException">selector is null.</exception>
        /// <exception cref="System.ArgumentException">source must have one or more elements.</exception>
        /// <exception cref="System.InvalidOperationException">Selector must return a numeric value.</exception>
        public static TSource ElementAtAverage <TSource, TSelector>(this IEnumerable <TSource> source, Func <TSource, TSelector> selector, AverageMatchType matchType)
        {
            if (source == null)
            {
                throw Error.ArgumentNull("source");
            }
            if (selector == null)
            {
                throw Error.ArgumentNull("selector");
            }
            TSource[] sourceArr = source.ToArray();
            Helper.InvalidateEmptySequence(sourceArr, "source");
            Helper.InvalidateNonNumeric <TSelector>("ElementAtAverage");

            TSelector tempSum = default(TSelector);
            int       count   = 0;

            foreach (TSource item in sourceArr)
            {
                if (count == 0)
                {
                    tempSum = selector(item);
                    count   = 1;
                    continue;
                }

                tempSum = Arithmetic.Helper <TSelector> .Add(tempSum, selector(item));

                count++;
            }

            decimal ts      = Convert.ToDecimal(tempSum);
            decimal tempAvg = ts / count;

            TSource minElement = default(TSource);
            TSource maxElement = default(TSource);
            decimal minValue   = 0;
            decimal maxValue   = 0;

            bool firstMin = true;
            bool firstMax = true;

            foreach (TSource item in sourceArr)
            {
                decimal temp = Convert.ToDecimal(selector(item));

                if (temp.CompareTo(tempAvg) <= 0)
                {
                    if (firstMin || minValue.CompareTo(temp) <= 0)
                    {
                        firstMin   = false;
                        minValue   = temp;
                        minElement = item;
                    }
                }

                if (temp.CompareTo(tempAvg) >= 0)
                {
                    if (firstMax || maxValue.CompareTo(temp) >= 0)
                    {
                        firstMax   = false;
                        maxValue   = temp;
                        maxElement = item;
                    }
                }
            }

            decimal distanceOfMaxValue = maxValue - tempAvg;
            decimal distanceOfMinValue = tempAvg - minValue;

            switch (matchType)
            {
            case AverageMatchType.Exact:
                if (distanceOfMaxValue == 0)
                {
                    return(maxElement);
                }
                return(default(TSource));

            case AverageMatchType.Closest:
                if (distanceOfMaxValue < distanceOfMinValue)
                {
                    return(maxElement);
                }
                return(minElement);

            case AverageMatchType.ExactOrLarger:
                return(maxElement);

            case AverageMatchType.ExactOrSmaller:
                return(minElement);

            default:
                throw Error.MatchType(matchType);
            }
        }