Home > Back-end >  How to check if one list contains all the elements of another list
How to check if one list contains all the elements of another list

Time:11-06

I want to check if one list contains all the elements of another list, for example:

(a,b,c,d) contains (c, a, d) = true
(a, b, c, d) contains (b, b, c, d) = false

I tried things like this:

static bool ContainsOther<T>(IEnumerable<T> a, IEnumerable<T> b)
{
    return new HashSet<T>(a).IsSupersetOf(new HashSet<T>(b));
}

But then it won't solve this correctly:
(a, b, c, d) contains (b, b, c, d) = false, it would say true, but I would want to receive false.

Same goes with nested loops.

CodePudding user response:

HashSet is a collection which contains unique elements:

The HashSet class provides high-performance set operations. A set is a collection that contains no duplicate elements, and whose elements are in no particular order.

So the behavior is expected. A quick an dirty approach can be using dictionary to group and count elements:

static bool ContainsOther<T>(IEnumerable<T> a, IEnumerable<T> b)
{
    var left = a.GroupBy(i => i)
        .ToDictionary(g => g.Key, g => g.Count());
    // or just cycle through elements of `b` and reduce count in a
    var right = b.GroupBy(i => i)
        .ToDictionary(g => g.Key, g => g.Count()); 

    foreach (var (key, value) in right)
    {
        if (!left.TryGetValue(key, out var leftValue) || leftValue < value)
            return false;
    }

    return true;
}

CodePudding user response:

So your enumerations can have duplicates and thus HashSet<T> is not an option (it keeps unique items only); let's use Dictionary instead (Key is an item, Value is its frequency):

static bool ContainsOther<T>(IEnumerable<T> left, 
                             IEnumerable<T> right, 
                             IEqualityComparer<T> comparer = default) {
  if (ReferenceEquals(left, right))
    return true;
  if (left is null)
    return false;
  if (right is null)
    return true;

  comparer ??= EqualityComparer<T>.Default;

  // left items with their frequencies 
  var dict = left
    .GroupBy(item => item, comparer)
    .ToDictionary(group => group.Key, group => group.Count(), comparer);

  // do we have an item in right which is not in dict with at least frequency?
  foreach (var item in right)
    if (dict.TryGetValue(item, out int count) && count > 0)
      dict[item] = count - 1; 
    else
      return false;
      
  return true;
}

CodePudding user response:

This works for me:

static bool ContainsOther<T>(IEnumerable<T> a, IEnumerable<T> b) =>
(
    from x in a.ToLookup(_ => _)
    join y in b.ToLookup(_ => _) on x.Key equals y.Key
    from z in x.Zip(y)
    select z
).Count() == b.Count();

It matches the values and ensures that the number of items for each key have the same length as in b.

Console.WriteLine(ContainsOther(new[] { 'a', 'b', 'c', 'd', }, new[] { 'c', 'a', 'd', }));
Console.WriteLine(ContainsOther(new[] { 'a', 'b', 'c', 'd', }, new[] { 'b', 'b', 'c', 'd', }));

Gives True and False as required.

CodePudding user response:

I would go with the two simple approaches.

List<string> ls1 = new List<string>() { "a", "b", "c" , "d"};
List<string> ls2 = new List<string>() { "c", "a", "y" };

bool isSuperset1 = ls1.Intersect(ls2).Count() == ls2.Count;
bool isSuperset2 = !ls2.Except(ls1).Any();
  • Related