Using Linq Except with two lists of int arrays - c#

Is it possible to use except with two lists of int arrays, like so:
List<int[]> a = new List<int[]>(){ new int[]{3,4,5}, new int[]{7,8,9}, new int[]{10,11,12} };
List<int[]> b = new List<int[]>(){ new int[]{6,7,9}, new int[]{3,4,5}, new int[]{10,41,12} };
var c = a.Except(b);
and exepecting {3,4,5} to be absent of the enumerable c? Of course I tried and this one is not working. Is there a solution as efficient as Except? Or even better, faster?

In .NET, arrays are only equal to another if they are the exact same array object. So two distinct arrays which have the same contents are not considered equal:
int[] x = new int[] { 1, 2 };
int[] y = new int[] { 1, 2 };
Console.WriteLine(x == y); // false
In order to check the equality based on the contents, you can use Enumerable.SequenceEqual:
Console.WriteLine(x.SequenceEqual(y)); // true
Of course that doesn’t help you directly when trying to use Enumerable.Except, since by default that will use the default equality comparer which only checks for equality (and since every array is inequal to every other array except itself…).
So the solution would be to use the other overload, and provide a custom IEqualityComparer which compares the arrays based on their content.
public class IntArrayEqualityComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] a, int[] b)
{
return a.SequenceEqual(b);
}
public int GetHashCode(int[] a)
{
return a.Sum();
}
}
Unfortunately, just delegating to SequenceEqual is not enough. We also have to provide a GetHashCode implementation for this to work. As a simple solution, we can use the sum of the numbers in the array here. Usually, we would want to provide a strong hash function, which tells a lot about the contents, but since we are only using this hash function for the Except call, we can use something simple here. (In general, we would also want to avoid creating a hash value from a mutable object)
And when using that equality comparer, we correctly filter out the duplicate arrays:
var c = a.Except(b, new IntArrayEqualityComparer());

That's because default EqualityComparer for int array returns false for to arrays with same values:
int[] a1 = { 1, 2, 3 };
int[] a2 = { 1, 2, 3 };
var ec = EqualityComparer<int[]>.Default;
Console.WriteLine(ec.Equals(a1, a2));//result is false
You can fix it by implementing your own EqualityComparer and passing its instance to Except method (see documentation).
You can also read about arrays comparison in C# here.

Related

Use a variable value as a part of array name

I've read other similar Questions. But, All of them use bash. IDK anything about that language.
The Thing I want to do is:
int i=0; //Value of i will change as I want to use it in loop
string name="c"+i;
double a= name[i]; //The real name of arrays I have declared are: c0[],c1[] etc
It gives error: "Project" does not contain a definition for "name"
So, How do I acheive this?
Obviously you have a set of variables, all sharing a common name, e.g. MyVariable1, MyVariable2, etc.
Instead of having so many similar variables, you should use an array, or in your case an array of arrays:
var myVariableArray = new double[][] { c[0], c[1], ... };
Now you can easily acces the i-th number within that array:
double a = myVariableArray[i][i];
Alternativly if those variables actually are members (fields or propertiers) within your class, you can also use reflection to get the right member from a string:
var fields = typeof(MyType).GetField(name + i);
double b[];
if(field != null)
b = (double[]) field.GetValue(instanceOfMyType, null);
else
{
var prop = typeof(MyType).GetProperty(name + i);
if(prop != null)
b = (double[]) prop.GetValue(instanceOfMyType, null);
}
a = b[i];
However such a data-structure is bad design, you should go with an array (or list) of members, instead of having dozens of similar members.
You need an array of arrays (two-dimensional arrays), don't you?
To create an array of arrays, do this:
double[][] twoDArray = new double[][x];
where x is the number of arrays you want.
Now you can populate it with some arrays like this:
twoDArray[0] = new double[] {1.0, 1.1, 1.2};
twoDArray[1] = new double[] {7.7, 8.8, 9.9};
To access an array in the 2D array, you don't even need name, you just use i directly!
double[] oneOfTheArrays = twoDArray[i];
double a = oneOfTheArray[0];
Or more simply:
double a = twoDArray[i][0];

Sort one List<> based on another

Say I have
List<int> ages = new List<int>() { 8, 5, 3, 9, 2, 1, 7 };
List<int> marks = new List<int>() { 12, 17, 08, 15, 19, 02, 11 };
I can sort my marks by ages like this:
while (true)
{
bool swapped = false;
for (int i = 0; i < ages.Count - 1; i++)
if (ages[i] > ages[i + 1])
{
int tmp = ages[i];
ages[i] = ages[i + 1];
ages[i + 1] = tmp;
tmp = marks[i];
marks[i] = marks[i + 1];
marks[i + 1] = tmp;
swapped = true;
}
if (!swapped)
break;
}
Now I want to put this into a function that accepts any two lists. The first parameter will be the reference list, the numerical or comparable list. The second parameter will be the list containing the data.
For example:
public static void Sort<T>(List<T> RefList, List<T> DataList)
{
// sorting logic here...
}
There are a few problems:
First of all, T is almost certainly not the same type in RefList and DataList. RefList might be dates, integers, or doubles; whereas DataList is free to be absolutely anything. I need to be able to receive two, arbitrary generic types.
Secondly, I cannot seem to use the > operator with the T in this line:
if (ages[i] > ages[i + 1])
Perhaps my whole approach is wrong.
By the way, I have read responses to similar questions that suggest that the two lists should be combined into a single list of a compound data type. This isn't practical at all for my application. All I want to do is write a static function that somehow sorts one list based on the elements of another.
To sort one list the way you want you actually need to somehow keep references from items in first list to they weight/keys in the second list. No existing methods do that as you can't easily associate metadata with arbitrary values (i.e. if first list is list of int as in your case there is nothing to map to keys in second list). Your only reasonable option is to sort 2 lists at the same time and make association by index - again no existing classes to help.
It may be much easier to use solution that you reject. I.e. simply Zip and OrderBy, than recreate first list:
ages = ages
.Zip(marks, (a,m)=> new {age = a; mark = m;})
.OrderBy(v => v.mark)
.Select(v=>v.age)
.ToList();
Note (courtesy of phoog): if you need to do this type of sorting with Array there is Array.Sort that allows exactly this operatiion (see phoog's answer for details).
There's no framework method to do this with List<T>, but if you don't mind putting the data into two arrays, you can use one of the Array.Sort() overloads that takes two arrays as arguments. The first array is the keys, and the second is the values, so your code might look like this (leaving aside the step of getting arrays from the lists):
Array.Sort(ages, marks);
The specifics of getting the values into arrays and then back into lists would depend, among other things, on whether you need to end up with the same list sorted appropriately or whether it's okay to return a new list with the data in the desired order.
Use:
public static void Sort<TR, TD>(IList<TR> refList, IList<TD> dataList)
where TR : System.IComparable<TR>
where TD : System.IComparable<TD>
{
...
}
and then use:
refList[i].CompareTo(refList[i+1])
instead of the operators.
.Net numbers already implement IComparable, and you can use overloads that allow you to specify a different IComparable.
If I understand "I can sort my marks by ages like this:" properly,
I would like to suggest the below to eliminate much confusion.
struct Student{
int age;
int marks;
};
List<Student> students = {{8,12}, ...};
Now you can sort according to age and marks is accordingly sorted automatically.
If it is not possible, you need to fix the code as below.
First of all, T is almost certainly not the same type in RefList and DataList.
Then you need 2 parameters T1, T2. Just T implies the types are the same.
public static void Sort<RefType, DataType>(List<RefType> RefList, List<DataType> DataList)
{
You can also zip the two lists together as suggested by Mechanical Snail and explained in Looping through 2 Lists at once

Deep Copy with Array

Why doesn't ICloneable's Clone method return a deep copy?
Here is some sample code:
class A : ICloneable
{
public int x = 2;
public A(int x)
{
this.x = x;
}
public A copy()
{
A a = new A(this.x);
return a;
}
public object Clone()
{
A a = new A(this.x);
return a;
}
}
In the Main method :
A[] Array1 = new A[4] ;
Array1[0] = new A(0);
Array1[1] = new A(1);
Array1[2] = new A(2);
Array1[3] = new A(3);
A[] Array2 = new A[10];
Array. Copy(Array1, Array2, 4);
Array2[2].x = 11;
for (int i = 0; i < 4; i++)
Console.WriteLine($"Index {i} value: {Array1[i].x}");
Remembering that I've only changed element index 2 in Array2, here is the output from the code above, listing the values in Array1:
Index 0 value: 0
Index 1 value: 1
Index 2 value: 11
Index 3 value: 3
Index 2 in Array1 has 11 even though I changed it in Array2 and class A implements ICloneable!
What then is the benefit of Array implementing ICloneable?
From http://msdn.microsoft.com/en-us/library/k4yx47a1.aspx:
"If sourceArray and destinationArray are both reference-type arrays or are both arrays of type Object, a shallow copy is performed. A shallow copy of an Array is a new Array containing references to the same elements as the original Array. The elements themselves or anything referenced by the elements are not copied"
There may be a better method than this, but one technique you could use is:
A[] array2 = array1.Select (a =>(A)a.Clone()).ToArray();
Array.Copy copies the values of the array, in this case, references. There is nothing in the documentation of Array.Copy() that indicates a check for classes that implement IClonable and call Clone() instead. You will need to loop through the array and call Clone() yourself.
BTW, yes, IClonable kind of sucks.
Array.Copy() does not use ICloneable. It simply copies values stored in each cell (which in this case are references to your A objects)
Ani's answer uses LINQ thus does not work for C# 2.0, however a same method can be done using the Array class's ConvertAll method:
A[] Array2 = Array.ConvertAll(Array1,a => (A)a.Clone());

Get the symmetric difference from generic lists

I have 2 separate List and I need to compare the two and get everything but the intersection of the two lists. How can I do this (C#)?
If you mean the set of everything but the intersection (symmetric difference) you can try:
var set = new HashSet<Type>(list1);
set.SymmetricExceptWith(list2);
You can use Except to get everything but the intersection of the two lists.
var differences = listA.Except(listB).Union(listB.Except(listA));
If you want to get everything but the union:
var allButUnion = new List<MyClass>();
(The union is everything in both lists - everything but the union is the empty set...)
Do you mean everything that's only in one list or the other? How about:
var allButIntersection = a.Union(b).Except(a.Intersect(b));
That's likely to be somewhat inefficient, but it fairly simply indicates what you mean (assuming I've interpreted you correctly, of course).
Here is a generic Extension method. Rosetta Code uses Concat, and Djeefther Souza says it's more efficient.
public static class LINQSetExtensions
{
// Made aware of the name for this from Swift
// https://stackoverflow.com/questions/1683147/get-the-symmetric-difference-from-generic-lists
// Generic implementation adapted from https://www.rosettacode.org/wiki/Symmetric_difference
public static IEnumerable<T> SymmetricDifference<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
// I've used Union in the past, but I suppose Concat works.
// No idea if they perform differently.
return first.Except(second).Concat(second.Except(first));
}
}
I haven't actually benchmarked it. I think it would depend on how Union vs. Concat are implemented. In my dreamworld, .NET uses a different algorithm depending on data type or set size, though for IEnumerable it can't determine set size in advance.
Also, you can pretty much ignore my answer - Jon Skeet says that the HashSet method "Excellent - that looks like the best way of doing it to me."
Something like this?
String[] one = new String[] { "Merry", "Metal", "Median", "Medium", "Malfunction", "Mean", "Measure", "Melt", "Merit", "Metaphysical", "Mental", "Menial", "Mend", "Find" };
String[] two = new String[] { "Merry", "Metal", "Find", "Puncture", "Revise", "Clamp", "Menial" };
List<String> tmp = one.Except(two).ToList();
tmp.AddRange(two.Except(one));
String[] result = tmp.ToArray();
var theUnion = list1.Concat(list2);
var theIntersection = list1.Intersect(list2);
var theSymmetricDifference = theUnion.Except(theIntersection);
Use Except:
List<int> l1 = new List<int>(new[] { 1, 2, 3, 4 });
List<int> l2 = new List<int>(new[] { 2, 4 });
var l3 = l1.Except(l2);

Convert an array of 'enum' to an array of 'int'

I'm trying to convert an Enum array to an int array:
public enum TestEnum
{
Item1,
Item2
}
int[] result = Array.ConvertAll<TestEnum, int>(enumArray, new Converter<TestEnum, int>(Convert.ToInt32));
For some reason Convert.ToInt32 doesn't work when used in Array.ConvertAll, so I had to make some changes:
int[] result = Array.ConvertAll<TestEnum, int>(enumArray, new Converter<TestEnum, int>(ConvertTestEnumToInt));
public static int ConvertTestEnumToInt(TestEnum te)
{
return (int)te;
}
Just out of curiosity, is there any way to have this working without using an extra method?
Regards
Just cast using an anonymous method:
int[] result = Array.ConvertAll<TestEnum, int>(
enumArray, delegate(TestEnum value) {return (int) value;});
or with C# 3.0, a lambda:
int[] result = Array.ConvertAll(enumArray, value => (int) value);
Luckily for us, C# 3.0 includes a Cast operation:
int[] result = enumArray.Cast<int>().ToArray();
If you stop using arrays and start using IEnumerable<>, you can even get rid of the ToArray() call.
enumArray.Select(x => (int) x)).ToArray()
This worked like a charm:
var intArray = enumArray.Select(e => (int)e).ToArray();
as did this:
var intList = enumArray.Select(e => (int)e).ToList();
FYI: tested on .Net4ClientProfile and VS2010
Actually, you don't even need to use LINQ. You can just cast it in a normal way, provided that you drop the type down to object.
Having:
enum One { one0, one1, one2, one3 };
enum Two { two0, two1, two2, two3 };
One[] vals = new One[] { One.one0, One.one3 };
we can play:
//Two[] aa = (Two[])vals; // impossible
Two[] aa = (Two[])(object)vals; // possible!
Two bb = aa[1]; // == Two.two3
At first, I was really suprised that the second line doesn't throw InvalidCast. But it does not.
Looking at the types explains things a little:
bool check2 = bb.GetType().FullName == "Two"; // well, you'd guess that
bool check3 = aa.GetType().FullName == "One[]"; // what?!
Seriously! The aa array is not Two[]. The array type has been "lost by variable" when it was cast to object, but both vals and (object)vals of course still refer to the same object. Then, after the following cast, it's still the aa object, the original array of One[], just hidden behind a new variable type.
The bb object/variable has type of Two because the array's item read as an item of a Two[] array (along with the variable's types). The real array One[] is concealed by the Two[] type, so indexing the Two[] must result in value of Two type.
Furthermore, since the actual type is hidden and since Enum types seem to be treated lightly, let's check another thing:
var numbers = (int[])(object)vals;
var cc = numbers[0] + 10; // == 10, from one0's value
var dd = numbers[1] + 10; // == 13, from one3's value
and similarly:
bool check4 = numbers.GetType().FullName == "One[]"; // not System.Int32[]
So, as you might already guess, the other way around is possible too:
var numbers2 = new int[]{ 0, 2, 99 };
var enums = (One[])(object)numbers2;
One ee = enums[0]; // == One.one0
One ff = enums[1]; // == One.one2
One gg = enums[2]; // == ((One)99)
and int[] also remembers its real type, even if casted to One[]:
bool check5 = numbers2.GetType().FullName == "System.Int32[]";
Even further, you cannot trust the as and is operators when the array is passed as object:
bool really_true1 = vals is One[];
bool really_true2 = vals is Two[];
bool really_true3 = vals is System.LoaderOptimization[];
This one was a 'gotha!' for me recently.
It actually reuses the original array object (instead of duplicating it like with LINQ) and it exposes it as different type - be it Two[] or int[]. It's seems to be a "true cast", no boxing. Much different than LINQ-driven copying&conversion.
int[] b = Array.ConvertAll((int[])Enum.GetValues(typeof(TestEnum)), Convert.ToInt32);

Categories

Resources