I have two arrays. For example:
int[] Array1 = new[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] Array2 = new[] {9, 1, 4, 5, 2, 3, 6, 7, 8};
What is the best way to determine if they have the same elements?
I have two arrays. For example:
int[] Array1 = new[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] Array2 = new[] {9, 1, 4, 5, 2, 3, 6, 7, 8};
What is the best way to determine if they have the same elements?
Assuming that the values in the array are unique, you can implement a performant solution using LINQ:
// create a LINQ query that matches each item in ar1 with
// its counterpart in ar2. we select "1" because we're only
// interested in the count of pairs, not the values.
var q = from a in ar1
join b in ar2 on a equals b
select 1;
// if lengths of the arrays are equal and the count of matching pairs
// is equal to the array length, then they must be equivalent.
bool equals = ar1.Length == ar2.Length && q.Count() == ar1.Length;
// when q.Count() is called, the join in the query gets translated
// to a hash-based lookup code which can run faster than nested
// for loops.
join
s. see https://mcmap.net/q/247517/-is-linq-join-operator-using-nested-loop-merge-or-hashset-joins for details about its behavior. –
Herrah You could also use SequenceEqual
, provided the IEnumerable objects are sorted first.
int[] a1 = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] a2 = new[] { 9, 1, 4, 5, 2, 3, 6, 7, 8 };
bool equals = a1.OrderBy(a => a).SequenceEqual(a2.OrderBy(a => a));
Assuming that the values in the array are unique, you can implement a performant solution using LINQ:
// create a LINQ query that matches each item in ar1 with
// its counterpart in ar2. we select "1" because we're only
// interested in the count of pairs, not the values.
var q = from a in ar1
join b in ar2 on a equals b
select 1;
// if lengths of the arrays are equal and the count of matching pairs
// is equal to the array length, then they must be equivalent.
bool equals = ar1.Length == ar2.Length && q.Count() == ar1.Length;
// when q.Count() is called, the join in the query gets translated
// to a hash-based lookup code which can run faster than nested
// for loops.
join
s. see https://mcmap.net/q/247517/-is-linq-join-operator-using-nested-loop-merge-or-hashset-joins for details about its behavior. –
Herrah Will the values always be unique? If so, how about (after checking equal length):
var set = new HashSet<int>(array1);
bool allThere = array2.All(set.Contains);
IStructuralEquatable
(tuples and arrays). So when should I choose IStructuralEquatable
vs SequenceEqual
? –
Pesce var shared = arr1.Intersect(arr2);
bool equals = arr1.Length == arr2.Length && shared.Count() == arr1.Length;
Use extension methods (which are new in 3.0). If the length of the Intersection of the two arrays equals that of their Union then the arrays are equal.
bool equals = arrayA.Intersect(arrayB).Count() == arrayA.Union(arrayB).Count()
Succinct.
For the most efficient approach (Reflectored from Microsoft code), see Stack Overflow question Comparing two collections for equality irrespective of the order of items in them.
Framework 4.0 introduced IStructuralEquatable interface which helps to compare types such as arrays or tuples:
class Program
{
static void Main()
{
int[] array1 = { 1, 2, 3 };
int[] array2 = { 1, 2, 3 };
IStructuralEquatable structuralEquator = array1;
Console.WriteLine(array1.Equals(array2)); // False
Console.WriteLine(structuralEquator.Equals(array2, EqualityComparer<int>.Default)); // True
// string arrays
string[] a1 = "a b c d e f g".Split();
string[] a2 = "A B C D E F G".Split();
IStructuralEquatable structuralEquator1 = a1;
bool areEqual = structuralEquator1.Equals(a2, StringComparer.InvariantCultureIgnoreCase);
Console.WriteLine("Arrays of strings are equal:"+ areEqual);
//tuples
var firstTuple = Tuple.Create(1, "aaaaa");
var secondTuple = Tuple.Create(1, "AAAAA");
IStructuralEquatable structuralEquator2 = firstTuple;
bool areTuplesEqual = structuralEquator2.Equals(secondTuple, StringComparer.InvariantCultureIgnoreCase);
Console.WriteLine("Are tuples equal:" + areTuplesEqual);
IStructuralComparable sc1 = firstTuple;
int comparisonResult = sc1.CompareTo(secondTuple, StringComparer.InvariantCultureIgnoreCase);
Console.WriteLine("Tuples comarison result:" + comparisonResult);//0
}
}
I have found the solution detailed here to be a very clean way, though a bit verbose for some people.
The best thing is that it works for other IEnumerables as well.
This will check that each array contains the same values in order.
int[] ar1 = { 1, 1, 5, 2, 4, 6, 4 };
int[] ar2 = { 1, 1, 5, 2, 4, 6, 4 };
var query = ar1.Where((b, i) => b == ar2[i]);
Assert.AreEqual(ar1.Length, query.Count());
public static bool ValueEquals(Array array1, Array array2)
{
if( array1 == null && array2 == null )
{
return true;
}
if( (array1 == null) || (array2 == null) )
{
return false;
}
if( array1.Length != array2.Length )
{
return false;
}
if( array1.Equals(array2))
{
return true;
}
else
{
for (int Index = 0; Index < array1.Length; Index++)
{
if( !Equals(array1.GetValue(Index), array2.GetValue(Index)) )
{
return false;
}
}
}
return true;
}
There are, of course, many ways to compare arrays based on the structure. To add more to the answers above, you can write your own custom comparers. Let's say you want to check whether both, the 2 arrays, contain even elements - you define your comparison based on the business rules of your application, that's why it's so subjective.
Here is a way to do it, writing your own comparer. Please note that there is no much care about the GetHashCode() method and you have to write your custom equality logic, at this moment, based on the default behavior (compare reference types) .equals() will give you different results if you use another collection to hold the arrays and we are saying that these 2 arrays contain even numbers and they are therefore equal, but we are breaking the rule that "If two values x and y evaluate equal then they MUST have the same hashcode". Don't worry too much here because we are comparing integers. With that said here is the example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp5
{
class EvenComparer : EqualityComparer<int>
{
public override bool Equals(int x, int y)
{
if((x % 2 == 0 && y % 2 == 0))
{
return true;
}
return false;
}
public override int GetHashCode(int obj)
{
return obj.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
//If you want to check whether the arrays are equal in the sense of containing the same elements in the same order
int[] Array1 = { 2, 4, 6};
int[] Array2 = {8, 10, 12 };
string[] arr1 = { "Jar Jar Binks", "Kill! Kill!", "Aaaaargh!" };
string[] arr2 = { "Jar Jar Binks", "Kill! Kill!", "Aaaaargh!" };
bool areEqual = (arr1 as IStructuralEquatable).Equals(arr2, StringComparer.Ordinal);
bool areEqual2 = (Array1 as IStructuralEquatable).Equals(Array2, new EvenComparer());
Console.WriteLine(areEqual);
Console.WriteLine(areEqual2);
Console.WriteLine(Array1.GetHashCode());
Console.WriteLine(Array2.GetHashCode());
}
}
}
After reading the answers I realize that nobody specified that you have to include the
using System.Collections;
namespace or you will get a missing using directive or assembly reference error for using the IStructuralEquatable interface.
Hope it helps someone someday
© 2022 - 2024 — McMap. All rights reserved.