/// <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)); } }
/// <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); } }