.NET проверяет, имеют ли два IEnumerable <T> те же элементы
Возможный дубликат:
Сравнение двух коллекций для равенства
Мне нужно проверить, имеют ли два IEnumerable<T>
списки одинаковые элементы, не обязательно в том же порядке.
Я нацелен на .NET 3.5.
Вот тесты. Вопрос в том, как реализовать HasSameElements()
?
var l1 = new[]{1,2,3};
var l2 = new[]{3,1,2};
bool rez1 = l1.HasSameElements(l2);//should be true
var l3 = new[]{1,2,3,2};
var l4 = new[]{3,1,2,2};
bool rez2 = l3.HasSameElements(l4);//should be true
var l5 = new[]{1,2,3,2};
var l6 = new[]{1,2,3};
bool rez3 = l5.HasSameElements(l6);//should be false
Дополнительные примечания:
-
В примерах я использую IEnumerable, но T может быть чем угодно. Должен ли T обязательно реализовать IComparable
?
-
Enumerable.SequenceEquals() сам по себе не работает, он ожидает того же порядка для элементов.
-
Здесь шаблон для HasElements
:
[только какой-то текст-заполнитель в качестве обходного пути для отформатирования кода Markdown]
public static class Extensions {
public static bool HasElements(this IEnumerable<T> l1, IEnumerable<T> l2){
throw new NotImplementedException();
}
}
Ответы
Ответ 1
Просто создайте словарь, сопоставляющий каждый объект с количеством раз, которое он появляется в последовательности, а затем проверьте, что результирующие словари равны.
Здесь:
static class EnumerableExtensions {
public static bool HasSameElementsAs<T>(
this IEnumerable<T> first,
IEnumerable<T> second
) {
var firstMap = first
.GroupBy(x => x)
.ToDictionary(x => x.Key, x => x.Count());
var secondMap = second
.GroupBy(x => x)
.ToDictionary(x => x.Key, x => x.Count());
return
firstMap.Keys.All(x =>
secondMap.Keys.Contains(x) && firstMap[x] == secondMap[x]
) &&
secondMap.Keys.All(x =>
firstMap.Keys.Contains(x) && secondMap[x] == firstMap[x]
);
}
}
Очевидно, повторный код может быть реорганизован в вспомогательные методы, но это просто путается с идеей здесь. Вы можете получить фантазию и принять IEqualityComparer
для операции GroupBy
. Кроме того, вы должны создать код, добавив null
охранники, а что нет.
Ответ 2
Несмотря на то, что метод Cristi Except
более эффективен, вы могли бы избежать:
source.Sort().SequenceEqual(target.Sort());
Если это для модульных тестов, я бы не стал беспокоиться о производительности. Конечно, вы хотите убедиться, что ваш сорт стабилен.
Ответ 3
Использование:
return l2.Count() == l1.Count() && l1.Intersect(l2).Count() == l2.Count();
Вы также можете передать пользовательский сопоставитель.
public static class Extensions
{
public static bool HasElements(
this IEnumerable<T> l1,
IEnumerable<T> l2,
IEqualityComparer<T> comparaer)
{
l2.Count() == l1.Count() &&
return l1.Intersect(l2, comparer).Count() == l2.Count();
}
}
Ответ 4
Я бы сделал что-то вроде этого
public static bool HasSameElements<T>(this IEnumerable<T> source, IEnumerable<T> target)
{
return (source.Count() == target.Count() && source.All(a => target.Contains(a)));
}
Ответ 5
Поскольку входы могут содержать дубликаты, вы не можете использовать Except. Один алгоритм:
if the lists contain a different number of elements return false
make a copy of listA
foreach element in listB
if the element exists in copyA remove the first match from copyA
otherwise return false
Для реализации вы можете посмотреть на логику метода ShouldBeEqualTo() в FluentAssert.