2
\$\begingroup\$

I got some amazing feedback on my previous question and I decided to extend this method and add it to my own library where every now and than I will add some methods I feel can be useful in the future.

There are currently 8 overloads with only 4 unique because there is a separate overload for each method where there is an extra argument which accepts custom IEqualityComparer<T>.

I have added some overloads where you can pass delegates for the case when you want to modify the whole object if it contains some indicator.

Those are the main methods:

#region Replace

#region IEnumerable<T>

/// <summary>
/// Replaces each element with specified value with a new one.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="oldValue">Old value to be replaced</param>
/// <param name="newValue">Action to invoke when oldValue is found</param>
/// <param name="comparer">Determines equality of the values</param>
/// <returns></returns>
[Pure]
public static IEnumerable<T> Replace<T>(this IEnumerable<T> source, T oldValue, Func<T, T> newValue,
    IEqualityComparer<T> comparer)
{
    if (source == null)
    {
        throw new ArgumentNullException(nameof(source));
    }
    if (comparer == null)
    {
        throw new ArgumentNullException(nameof(comparer));
    }
    if (newValue == null)
    {
        throw new ArgumentNullException(nameof(newValue));
    }

    return source.Select(value => comparer.Equals(value, oldValue)
        ? newValue.Invoke(value)
        : value);
}

/// <summary>
/// Replaces each element with specified value with a new one.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="oldValue">Old value to be replaced</param>
/// <param name="newValue">Value to replace the old one</param>
/// <param name="comparer">Determines equality of the values</param>
/// <returns></returns>
[Pure]
public static IEnumerable<T> Replace<T>(this IEnumerable<T> source, T oldValue, T newValue,
    IEqualityComparer<T> comparer)
{
    return Replace(source, oldValue, t => newValue, comparer);
}

/// <summary>
/// Replaces each element with specified value with a new one.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="oldValue">Old value to be replaced</param>
/// <param name="newValue">Value to replace the old one</param>
/// <returns></returns>
[Pure]
public static IEnumerable<T> Replace<T>(this IEnumerable<T> source, T oldValue, T newValue)
{
    return Replace(source, oldValue, newValue, EqualityComparer<T>.Default);
}

/// <summary>
/// Replaces each element with specified value with a new one.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="oldValue">Old value to be replaced</param>
/// <param name="newValue">Action to invoke when oldValue is found</param>
/// <returns></returns>
[Pure]
public static IEnumerable<T> Replace<T>(this IEnumerable<T> source, T oldValue, Func<T, T> newValue)
{
    return Replace(source, oldValue, newValue, EqualityComparer<T>.Default);
}

/// <summary>
/// Replaces each element with a value fetched from a collection of key/value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="oldToNewValues">The collection from which replaced values will be taken. Key stands for the old value and the Value for action to be invoked.</param>
/// <param name="comparer">Determines equality of the values</param>
/// <returns></returns>
[Pure]
public static IEnumerable<T> Replace<T>(this IEnumerable<T> source,
    IEnumerable<KeyValuePair<T, Func<T, T>>> oldToNewValues, IEqualityComparer<T> comparer)
{
    if (source == null)
    {
        throw new ArgumentNullException(nameof(source));
    }
    if (comparer == null)
    {
        throw new ArgumentNullException(nameof(comparer));
    }
    if (oldToNewValues == null)
    {
        throw new ArgumentNullException(nameof(oldToNewValues));
    }

    return oldToNewValues.Aggregate(source,
        (current, value) => current.Replace(value.Key, value.Value, comparer));
}

/// <summary>
/// Replaces each element with a value fetched from a collection of key/value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="oldToNewValues">The collection from which replaced values will be taken. Key stands for the old value and the Value for action to be invoked.</param>
/// <returns></returns>
[Pure]
public static IEnumerable<T> Replace<T>(this IEnumerable<T> source,
    IEnumerable<KeyValuePair<T, Func<T, T>>> oldToNewValues)
{
    return Replace(source, oldToNewValues, EqualityComparer<T>.Default);
}

/// <summary>
/// Replaces each element with a value fetched from a collection of key/value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="oldToNewValues">The collection from which replaced values will be taken. Key stands for the old value and the Value for the new value.</param>
/// <param name="comparer">Determines equality of the values</param>
/// <returns></returns>
[Pure]
public static IEnumerable<T> Replace<T>(this IEnumerable<T> source,
    IEnumerable<KeyValuePair<T, T>> oldToNewValues, IEqualityComparer<T> comparer)
{
    return Replace(source,
        oldToNewValues.Select(
            oldToNewValue => new KeyValuePair<T, Func<T, T>>(oldToNewValue.Key, t => oldToNewValue.Value)),
        comparer);
}

/// <summary>
/// Replaces each element with a value fetched from a collection of key/value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="oldToNewValues">The collection from which replaced values will be taken. Key stands for the old value and the Value for the new value.</param>
/// <returns></returns>
[Pure]
public static IEnumerable<T> Replace<T>(this IEnumerable<T> source,
    IEnumerable<KeyValuePair<T, T>> oldToNewValues)
{
    return Replace(source, oldToNewValues, EqualityComparer<T>.Default);
}

#endregion

#endregion

I've tried to implement almost all of the suggestions in the previous question and I've been reusing my code as much as possible. Since this will be in my dll, I've also documented the code.

Feel free to comment on anything you feel like can be improved.

Here are some of my questions:

  1. I'm not familiar with the proper ways of documenting my code e.g referencing variables in the description and overall clarity of the documentation.

  2. Is it appropriate to have the overloads which accept delegate? They are modifying the collection/item, it sounds more like a separate method ('Modify'), what are your thoughts?

  3. How well have I reused my code? This line:

    return Replace(source,
        oldToNewValues.Select(
        oldToNewValue => new KeyValuePair<T, Func<T, T>>(oldToNewValue.Key, t => oldToNewValue.Value)),
    comparer);
    

    Can also be written like this:

    return oldToNewValues.Aggregate(source, (current, kvp) => current.Replace(kvp.Key, kvp.Value, comparer));
    

    But this would require to write the null checks in this method too.

\$\endgroup\$
1
\$\begingroup\$

Is it appropriate to have the overloads which accept delegate?

I think nothing speaks against it.


They are modifying the collection/item, it sounds more like a separate method ('Modify'), what are your thoughts?

They are not modifying anything yet. They just express that a Func requires a T and returns a T. Whether it's the same or a new object is up to the implementetion. If those methods are [Pure] as well, then everything is fine.


How well have I reused my code?

Too good. I don't know what I should complain about but let me try...

There is nothing wrong with the implementation, it looks actually very good but there are two things that make me think.

  1. There are 8 overloads. That's a lot. Do you really need all of them? I think there might be issues with other parts of your code if that many overloads are necessary.
  2. #region IEnumerable<T> this region is suspicious, it suggests this might be g god-class for extensions. If I'm right you should split it up.
\$\endgroup\$
  • \$\begingroup\$ It's stored in IEnumerableExtensions I thought it would've been better to have all of the overloads of the method in 1 class, but it makes sense to have a separate one for IList<T> and other interfaces. \$\endgroup\$ – Denis Jun 20 '17 at 14:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.