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:
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.
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?
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.