如何检查一个列表是否包含另一个列表的所有元素?

3
我想检查一个列表是否包含另一个列表的所有元素,例如:
(a,b,c,d) 包含 (c, a, d) = true (a, b, c, d) 包含 (b, b, c, d) = false 我尝试了这样的方法:
static bool ContainsOther<T>(IEnumerable<T> a, IEnumerable<T> b)
{
    return new HashSet<T>(a).IsSupersetOf(new HashSet<T>(b));
}

但这样做将无法正确解决这个问题:
(a, b, c, d) 包含 (b, b, c, d) = false,它会返回 true,但我想要得到的是false
嵌套循环同理。
4个回答

2

HashSet是一个包含唯一元素的集合:

HashSet类提供高性能的集合操作。集合是一组不包含重复元素且元素没有特定顺序的对象。

因此,这种行为是可以预期的。一种快速且简单的方法是使用字典来对元素进行分组和计数:

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;
}

1
不应该使用 GroupBy,而是更有效地使用 MoreLinq 包中的 CountBy 运算符。 - Theodor Zoulias

1

所以你的枚举可以有重复,因此HashSet<T>不是一个选项(它仅保留唯一的项目);我们将使用Dictionary代替(Key是一个项目,Value是它的频率):

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;
}

1
这对我有效:
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();

它匹配值并确保每个键的项目数量与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', }));

根据需要提供TrueFalse


0

我会选择两种简单的方法。

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();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接