Skip to content

Instantly share code, notes, and snippets.

@beyond-code-github
Last active January 4, 2016 01:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save beyond-code-github/8549366 to your computer and use it in GitHub Desktop.
Save beyond-code-github/8549366 to your computer and use it in GitHub Desktop.
Efficient Iterator sample
public class EfficientIterator<TOne, TTwo>
{
private Action<TOne, TTwo> always = (one, two) => { };
private Action<TOne, TTwo> match = (one, two) => { };
private Action<TOne> oneOnly = (one) => { };
private Action<TTwo> twoOnly = (two) => { };
private readonly Func<TOne, TTwo, int> compare;
public Action<TOne, TTwo> Always { set { this.always = value; } }
public Action<TOne, TTwo> Match { set { this.match = value; } }
public Action<TOne> OneOnly { set { this.oneOnly = value; } }
public Action<TTwo> TwoOnly { set { this.twoOnly = value; } }
public EfficientIterator(Func<TOne, TTwo, int> compare)
{
this.compare = compare;
}
public void RunToEnd(IEnumerable<TOne> listOne, IEnumerable<TTwo> listTwo)
{
using (var listOneEnumerator = listOne.GetEnumerator())
using (var listTwoEnumerator = listTwo.GetEnumerator())
{
var listOneIterating = listOneEnumerator.MoveNext();
var listTwoIterating = listTwoEnumerator.MoveNext();
while (listOneIterating || listTwoIterating)
{
int result = 0;
if (listOneIterating && listTwoIterating)
{
result = this.compare(listOneEnumerator.Current, listTwoEnumerator.Current);
// a and b are equal
if (result == 0)
{
this.always(listOneEnumerator.Current, listTwoEnumerator.Current);
this.match(listOneEnumerator.Current, listTwoEnumerator.Current);
listOneIterating = listOneEnumerator.MoveNext();
listTwoIterating = listTwoEnumerator.MoveNext();
continue;
}
}
// list one has run out, or it's current item is greater than list two
if (!listOneIterating || result > 0)
{
this.always(default(TOne), listTwoEnumerator.Current);
this.twoOnly(listTwoEnumerator.Current);
listTwoIterating = listTwoEnumerator.MoveNext();
continue;
}
// list two has run out, or it's current item is greater than list one
if (!listTwoIterating || result < 0)
{
this.always(listOneEnumerator.Current, default(TTwo));
this.oneOnly(listOneEnumerator.Current);
listOneIterating = listOneEnumerator.MoveNext();
continue;
}
}
}
}
}
@tommy-carlier
Copy link

Hi Roy, I forked your sample and did some modifications:

  • I removed the type constraints (where TOne : class, where TTwo : class) so value types can also be used (this required replacing null with default(TOne) and default(TTwo) when invoking the always-action with a missing value).
  • I changed the types of the arguments of RunToEnd from List<T> to IEnumerable<T>. This enables you to pass in collections of any type that implements IEnumerable<T>, not just List<T>.
  • I used the using-construct to properly dispose the enumerators after using them.

See https://gist.github.com/tommy-carlier/8556273 (or the above 3 changes as separate revisions).

@beyond-code-github
Copy link
Author

Awesome thanks! I'd just come on to make the change in the second point and seen that you've beaten me to it. Good job :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment