Ответ 1
Существует немало способов. Предположим, что A и B IEnumerable.
!A.Except(B).Any() && !B.Except(A).Any()
A.Count() == B.Count() && A.Intersect(B).Count() == B.Count()
etc
Есть ли встроенная функция метода linq, которую я могу использовать, чтобы узнать, содержат ли две последовательности одни и те же элементы, не принимая во внимание порядок?
Например:
{1, 2, 3} == {2, 1, 3}
{1, 2, 3} != {2, 1, 3, 4}
{1, 2, 3} != {1, 2, 4}
У вас есть SequenceEquals, но тогда мне придется сначала заказывать обе последовательности, не так ли?
Существует немало способов. Предположим, что A и B IEnumerable.
!A.Except(B).Any() && !B.Except(A).Any()
A.Count() == B.Count() && A.Intersect(B).Count() == B.Count()
etc
С двумя IEnumerables (A и B):
bool equal = (A.Count() == B.Count() && (!A.Except(B).Any() || !B.Except(A).Any()))
Я думаю, что это лучше, чем Except (A). Укажите, что весь Excep не будет оцениваться. Он остановится, как только один элемент будет найден в Except. С графом оценивается весь Except. Кроме того, мы можем избежать оценки этих затрат. Кроме того, сначала проверяя свойства Count. Если Counts не равны, тогда мы проверяем Excepts.
Если вам не нужны дубликаты (т.е. вы считаете {1, 2, 3}
равным {1, 2, 3, 2}
), то:
new HashSet<int>(A).SetEquals(B)
(Или любой тип - это тип элемента вместо int
).
В противном случае:
public static bool SequenceEqualUnordered<T>(IEnumerable<T> first, IEnumerable<T> second)
{
if (first == null)
return second == null; // or throw if that more appropriate to your use.
if (second == null)
return false; // likewise.
var dict = new Dictionary<T, int>(); // You could provide a IEqualityComparer<T> here if desired.
foreach(T element in first)
{
int count;
dict.TryGetValue(element, out count);
dict[element] = count + 1;
}
foreach(T element in second)
{
int count;
if (!dict.TryGetValue(element, out count))
return false;
else if (--count == 0)
dict.Remove(element);
else
dict[element] = count;
}
return dict.Count == 0;
}
Сохраняйте подсчет каждого элемента в первой последовательности, затем проверяйте второе против него. В тот момент, когда у вас слишком много во второй последовательности, вы можете вернуть false, иначе, если у вас ничего не осталось в словаре таблеток, они равны или ложны, если остались какие-либо элементы.
Вместо двух типов O (n log n), использующих OrderBy()
, за которым следует сравнение O (n), вы выполняете операцию O (n) с набором наборов и проверку O (n) против него.
Попробуйте класс HashSet:
var enumA = new[] { 1, 2, 3, 4 };
var enumB = new[] { 4, 3, 1, 2 };
var hashSet = new HashSet<int>(enumA);
hashSet.SymmetricExceptWith(enumB);
Console.WriteLine(hashSet.Count == 0); //true => equal
Но это работает правильно, если значения различны.
Например
var enumA = new[] { 1, 1, 1, 2 };
var enumB = new[] { 1, 2, 2, 2 };
также считаются "равными" с указанным методом.
Я думаю, что упорядочение последовательности - это самый быстрый способ добиться этого.
Я сделал это для слияния новых элементов в коллекцию без дубликатов, он берет две коллекции и возвращает все элементы без каких-либо дубликатов
List<Campaign> nonMatching = (from n in newCampaigns
where !(from e in Existing select e.Id).Contains<int>(n.Id)
select n).ToList<Campaign>();
Теперь, удалив! для оператора contains
List<Campaign> nonMatching = (from n in newCampaigns
where (from e in Existing select e.Id).Contains<int>(n.Id)
select n).ToList<Campaign>();
он вернет дубликаты
Если вы действительно просто проверяете, есть ли дубликаты, то предложение leppie должно работать:
if (A.Except(B).Count == 0 && B.Except(A).Count == 0) {...}
Но если вам просто нужно получить IEnumerable без дубликатов:
var result = A.Union(B).Distinct();
Придерживаясь своего примера, вы можете сделать так, чтобы оба IEnumerable имели тип List, а затем использовать SequenceEqual в качестве примера ниже:
var first = Enumerable.Range(1, 3);
var second = Enumerable.Range(1, 3);
var areTheyEqual = first.ToList().SequenceEqual(second.ToList());
if (areTheyEqual)
{ /* do something... */}