Question

Any idea on how to check whether that list is a subset of another?

Specifically, I have

List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };

How to check that t2 is a subset of t1, using LINQ?

Was it helpful?

Solution

bool isSubset = !t2.Except(t1).Any();

OTHER TIPS

Use HashSet instead of List if working with sets. Then you can simply use IsSubsetOf()

HashSet<double> t1 = new HashSet<double>{1,3,5};
HashSet<double> t2 = new HashSet<double>{1,5};

bool isSubset = t2.IsSubsetOf(t1);

Sorry that it doesn't use LINQ. :-(

If you need to use lists, then @Jared's solution works with the caveat that you will need to remove any repeated elements that exist.

If you are unit-testing you can also utilize the CollectionAssert.IsSubsetOf method :

CollectionAssert.IsSubsetOf(subset, superset);

In the above case this would mean:

CollectionAssert.IsSubsetOf(t2, t1);

@Cameron's solution as an extension method:

public static bool IsSubsetOf<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
    return !a.Except(b).Any();
}

Usage:

bool isSubset = t2.IsSubsetOf(t1);

(This is similar, but not quite the same as the one posted on @Michael's blog)

This is a significantly more efficient solution than the others posted here, especially the top solution:

bool isSubset = t2.All(elem => t1.Contains(elem));

If you can find a single element in t2 that isn't in t1, then you know that t2 is not a subset of t1. The advantage of this method is that it is done all in-place, without allocating additional space, unlike the solutions using .Except or .Intersect. Furthermore, this solution is able to break as soon as it finds a single element that violates the subset condition, while the others continue searching. Below is the optimal long form of the solution, which is only marginally faster in my tests than the above shorthand solution.

bool isSubset = true;
foreach (var element in t2) {
    if (!t1.Contains(element)) {
        isSubset = false;
        break;
    }
}

I did some rudimentary performance analysis of all the solutions, and the results are drastic. These two solutions are about 100x faster than the .Except() and .Intersect() solutions, and use no additional memory.

Building on the answers from @Cameron and @Neil I wrote an extension method that uses the same terminology as the Enumerable class.

/// <summary>
/// Determines whether a sequence contains the specified elements by using the default equality comparer.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <param name="source">A sequence in which to locate the values.</param>
/// <param name="values">The values to locate in the sequence.</param>
/// <returns>true if the source sequence contains elements that have the specified values; otherwise, false.</returns>
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> values)
{
    return !values.Except(source).Any();
}

Here we check that if there is any element present in the child list(i.e t2) which is not contained by the parent list(i.e t1).If none such exists then the list is subset of the other

eg:

bool isSubset = !(t2.Any(x => !t1.Contains(x)));

Try this

static bool IsSubSet<A>(A[] set, A[] toCheck) {
  return set.Length == (toCheck.Intersect(set)).Count();
}

The idea here is that Intersect will only return the values that are in both Arrays. At this point if the length of the resulting set is the same as the original set, then all elements in "set" are also in "check" and therefore "set" is a subset of "toCheck"

Note: My solution does not work if "set" has duplicates. I'm not changing it because I don't want to steal other people's votes.

Hint: I voted for Cameron's answer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top