Skip to content

Instantly share code, notes, and snippets.

@RobThree
Last active April 8, 2023 21:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RobThree/6318500 to your computer and use it in GitHub Desktop.
Save RobThree/6318500 to your computer and use it in GitHub Desktop.
Linq extensions: Diff, ToChunks and ExecuteChunked
using System;
using System.Linq;
using System.Collections.Generic;
//Sample program demonstrating the linq extensions (below Progam class)
class Program
{
static void Main(string[] args)
{
//Demonstrate Diff
var left = new[] { "Foo", "Apple", "Banana", "Bar", "Jane", "Purple", "42" };
var right = new[] { "John", "Cherry", "Foo", "Tuesday", "Blue", "Bar" };
var diff = left.Diff(right);
Console.WriteLine("In left only : {0}", string.Join(",", diff.LeftOnly));
Console.WriteLine("In right only : {0}", string.Join(",", diff.RightOnly));
Console.WriteLine("In both : {0}", string.Join(",", diff.Intersect));
//Create a slightly larger collection of items to toy with
var all = left.Union(right);
//Demonstrate ToChunks
var chunks = left.Union(right).ToChunks(3);
Console.WriteLine("In chunks of 3:\n\t{0}", string.Join("\n\t", chunks.Select(c => string.Join(",", c))));
//Demonstrate ExecuteChunked
left.Union(right).ExecuteChunked(3, (chunk) =>
{
Console.WriteLine("This is a chunk: {0}", string.Join(",", chunk));
});
}
}
namespace System.Linq //TODO: Change this to your needs!
{
/// <summary>
/// Provides a set of static (Shared in Visual Basic) methods for creating diffs and splitting items into
/// chunks (and optionally invoke an action on each chunk) on objects that implement System.Collections.Generic.IEnumerable&lt;T&gt;.
/// </summary>
public static class IEnumerableExtensions
{
/// <summary>
/// Creates a "Diff" of two IEnumerable&lt;T&gt;'s.
/// </summary>
/// <typeparam name="T">The type of elements.</typeparam>
/// <param name="left">The left part of the elements to compare to the right.</param>
/// <param name="right">The right part of the elements to compare to the left.</param>
/// <returns>Returns a <see cref="Diff&lt;T&gt;">Diff&lt;T&gt;</see></returns>
public static Diff<T> Diff<T>(this IEnumerable<T> left, IEnumerable<T> right)
{
return Diff(left, right, null);
}
/// <summary>
/// Creates a "Diff" of two IEnumerable&lt;T&gt;'s.
/// </summary>
/// <typeparam name="T">The type of elements.</typeparam>
/// <param name="left">The left part of the elements to compare to the right.</param>
/// <param name="right">The right part of the elements to compare to the left.</param>
/// <param name="comparer">An IEqualityComparer&lt;T&gt; to compare values.</param>
/// <returns>Returns a <see cref="Diff&lt;T&gt;">Diff&lt;T&gt;</see></returns>
public static Diff<T> Diff<T>(this IEnumerable<T> left, IEnumerable<T> right, IEqualityComparer<T> comparer)
{
var hsl = new HashSet<T>(left);
var hsr = new HashSet<T>(right);
return new Diff<T>(hsl.Except(hsr, comparer), hsr.Except(hsl, comparer), hsl.Intersect(hsr, comparer));
}
/// <summary>
/// Splits an IEnumerable&lt;T&gt; into chunks (or partitions) of the specified size.
/// </summary>
/// <typeparam name="T">The type of elements.</typeparam>
/// <param name="items">The items to split into chunks (partitions).</param>
/// <param name="chunksize">The size of the chunks to return.</param>
/// <returns>Returns an IEnumerable&lt;IEnumerable&lt;T&gt;&gt;; each chunk containing the items for that chunk.</returns>
public static IEnumerable<IEnumerable<T>> ToChunks<T>(this IEnumerable<T> items, int chunksize)
{
return items.Select((v, i) => new { Value = v, Index = i })
.GroupBy(x => x.Index / chunksize)
.Select(x => x.Select(y => y.Value));
}
/// <summary>
/// Takes an IEnumerable&lt;T&gt; of items, splits them into chunks (or partitions) and invokes an action on each chunk (or partition).
/// </summary>
/// <typeparam name="T">The type of elements.</typeparam>
/// <param name="items">The items to invoke the action on for each chunk of the items.</param>
/// <param name="chunksize">The size of the chunks for each action.</param>
/// <param name="action">The action to execute on each chunk.</param>
public static void ExecuteChunked<T>(this IEnumerable<T> items, int chunksize, Action<IEnumerable<T>> action)
{
foreach (var c in items.ToChunks(chunksize))
action.Invoke(c);
}
}
/// <summary>
/// Represents a diff result.
/// </summary>
/// <typeparam name="T"></typeparam>
public class Diff<T>
{
/// <summary>
/// Items that were only found in the left part.
/// </summary>
public HashSet<T> LeftOnly { get; private set; }
/// <summary>
/// Items that were only found in the right part.
/// </summary>
public HashSet<T> RightOnly { get; private set; }
/// <summary>
/// Items that were found in both the left and right part.
/// </summary>
public HashSet<T> Intersect { get; private set; }
/// <summary>
/// Initializes a new instance of the Diff&lt;T&gt; class.
/// </summary>
/// <param name="leftonly">Items that are only found in the left part.</param>
/// <param name="rightonly">Items that are only found in the right part.</param>
/// <param name="intersect">Items that are found in the both the left and right part.</param>
public Diff(IEnumerable<T> leftonly, IEnumerable<T> rightonly, IEnumerable<T> intersect)
{
this.LeftOnly = new HashSet<T>(leftonly);
this.RightOnly = new HashSet<T>(rightonly);
this.Intersect = new HashSet<T>(intersect);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment