Skip to main content
  1. Posts/

Sequence Membership

··3 mins

Given two sorted sequences, Membership returns the A ∩ B, A \ B, and B \ A. ObjectSetLocation specifies where the object lives.

/// <summary>
/// Identify in which set(s) the object lives
/// </summary>
/// <remarks>
/// Descriptions from http://en.wikipedia.org/wiki/Set_(mathematics)
/// </remarks>
public enum ObjectSetLocation : byte {
    /// <summary>
    /// The relative complement of B in A, denoted by A − B (or A \ B),
    /// is the set of all elements which are members of A but not
    /// members of B.
    /// </summary>
    AButNotB = 10,
    /// <summary>
    /// The intersection of A and B, denoted by A ∩ B, is the set
    /// of all things which are members of both A and B. If A ∩ B = ∅,
    /// then A and B are said to be disjoint.
    /// </summary>
    Intersection = 11,
    /// <summary>
    /// The relative complement of A in B, denoted by B − A (or B \ A),
    /// is the set of all elements which are members of B but not
    /// members of A.
    /// </summary>
    BButNotA = 12
}

The return type is an IEnumerable<Tuple<T, ObjectSetLocation>>, using a single iteration over both sequences:

/// <summary>
/// Return a tuple for every element in sortedSetA and sortedSetB specifying objects
/// that belong to A but not to B, objects which are both in A and in B, and objects
/// that belong to B but not A.
/// </summary>
/// <remarks>This method is an O(n+m) operation when the two sets have different members, where n
/// is the count of sortedSetA and m is the count of sortedSetB.
/// Otherwise the operation approaches O(n+m-n) where m is the count of the larger set and n is the
/// smaller set.
/// </remarks>
/// <typeparam name="T">Type of element in each set</typeparam>
/// <param name="sortedSetA">A sorted set where each element is of type T</param>
/// <param name="sortedSetB">A sorted set where each element is of type T</param>
/// <param name="comparer">IComparer used to sort the sets</param>
/// <returns>Returns A \ B (-1), the intersection of A and B (0), and B \ A (1)</returns>
public static IEnumerable<Tuple<T, ObjectSetLocation>> Membership<T> (this IEnumerable<T> sortedSetA, IEnumerable<T> sortedSetB, IComparer<T> comparer = null) {
    Contract.Requires (sortedSetA != null);
    Contract.Requires (sortedSetB != null);

    if (null == comparer)
        comparer = Comparer<T>.Default;

    using (var enumeratorA = sortedSetA.GetEnumerator ())
    using (var enumeratorB = sortedSetB.GetEnumerator ()) {
        bool moveNextA = enumeratorA.MoveNext ();
        bool moveNextB = enumeratorB.MoveNext ();
        T currentA = default (T);
        T currentB = default (T);
        if (moveNextA & moveNextB) {
            currentA = enumeratorA.Current;
            currentB = enumeratorB.Current;
            do {
                int compareResult = comparer.Compare (currentA, currentB);
                if (compareResult == 0) {
                    yield return new Tuple<T, ObjectSetLocation> (currentA, ObjectSetLocation.Intersection);
                    if (moveNextA = enumeratorA.MoveNext ())
                        currentA = enumeratorA.Current;
                    if (moveNextB = enumeratorB.MoveNext ())
                        currentB = enumeratorB.Current;
                }
                else if (compareResult < 0) {
                    yield return new Tuple<T, ObjectSetLocation> (currentA, ObjectSetLocation.AButNotB);
                    if (moveNextA = enumeratorA.MoveNext ())
                        currentA = enumeratorA.Current;
                }
                else {
                    yield return new Tuple<T, ObjectSetLocation> (currentA, ObjectSetLocation.BButNotA);
                    if (moveNextB = enumeratorB.MoveNext ())
                        currentB = enumeratorB.Current;
                }
            }
            while (moveNextA & moveNextB);
        }
        if (moveNextA) {
            yield return new Tuple<T, ObjectSetLocation> (currentA, ObjectSetLocation.AButNotB);
            while (enumeratorA.MoveNext ()) {
                yield return new Tuple<T, ObjectSetLocation> (enumeratorA.Current, ObjectSetLocation.AButNotB);
            }
        }
        else if (moveNextB) {
            yield return new Tuple<T, ObjectSetLocation> (currentA, ObjectSetLocation.BButNotA);
            while (enumeratorB.MoveNext ()) {
                yield return new Tuple<T, ObjectSetLocation> (enumeratorB.Current, ObjectSetLocation.BButNotA);
            }
        }
    }
}
George Tsiokos
Author
George Tsiokos

Leave a comment

Preview

Comments are reviewed before publishing.