Ответ 1
bool isSubset = !t2.Except(t1).Any();
Любая идея о том, как проверить, является ли этот список подмножеством другого?
В частности, у меня есть
List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };
Как проверить, что t2 является подмножеством t1, используя LINQ?
bool isSubset = !t2.Except(t1).Any();
Используйте HashSet вместо List, если работаете с наборами. Затем вы можете просто использовать IsSubsetOf()
HashSet<double> t1 = new HashSet<double>{1,3,5};
HashSet<double> t2 = new HashSet<double>{1,5};
bool isSubset = t2.IsSubsetOf(t1);
Извините, что он не использует LINQ.: - (
Если вам нужно использовать списки, то решение @Jared работает с оговоркой, что вам нужно будет удалить любые повторяющиеся элементы, которые существуют.
Если вы unit-testing, вы также можете использовать метод CollectionAssert.IsSubsetOf:
CollectionAssert.IsSubsetOf(subset, superset);
В приведенном выше случае это означало бы:
CollectionAssert.IsSubsetOf(t2, t1);
@Решение Cameron как метод расширения:
public static bool IsSubsetOf<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
return !a.Except(b).Any();
}
Использование:
bool isSubset = t2.IsSubsetOf(t1);
(Это похоже, но не совсем то же самое, что и в блоге @Michael)
Это значительно более эффективное решение, чем другие, размещенные здесь, особенно верхнее решение:
bool isSubset = t2.All(elem => t1.Contains(elem));
Если вы можете найти один элемент в t2, который не находится в t1, то вы знаете, что t2 не является подмножеством t1. Преимущество этого метода заключается в том, что он выполняется на месте, без выделения дополнительного пространства, в отличие от решений, использующих .Except или .Intersect. Кроме того, это решение может сломаться, как только он найдет один элемент, который нарушает условие подмножества, в то время как другие продолжают поиск. Ниже приведена оптимальная длинная форма решения, которая в моих тестах только немного быстрее, чем указанное сокращенное решение.
bool isSubset = true;
foreach (var element in t2) {
if (!t1.Contains(element)) {
isSubset = false;
break;
}
}
Я провел некоторый рудиментарный анализ эффективности всех решений, и результаты были радикальными. Эти два решения примерно в 100 раз быстрее, чем решения .Except() и .Intersect(), и не используют дополнительную память.
Попробуйте это
static bool IsSubSet<A>(A[] set, A[] toCheck) {
return set.Length == (toCheck.Intersect(set)).Count();
}
Идея здесь заключается в том, что Intersect вернет только значения, которые находятся в обоих массивах. В этом случае, если длина результирующего набора совпадает с исходным, все элементы в "set" также находятся в "проверке", и поэтому "set" является подмножеством "toCheck"
Примечание. Мое решение не работает, если "set" имеет дубликаты. Я не меняю его, потому что я не хочу воровать других людей.
Подсказка: я проголосовал за ответ Кэмерона.
Основываясь на ответах @Cameron и @Neil, я написал метод расширения, который использует ту же терминологию, что и класс Enumerable.
/// <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();
}
Здесь мы проверяем, есть ли какой-либо элемент, присутствующий в дочернем списке (т.
t2
), который не содержится в родительском списке (т.t1
). Если ни один из них не существует, список является подмножеством другого
например:
bool isSubset = !(t2.Any(x => !t1.Contains(x)));