Reputation: 689
I have two Lists:
List<L1>, List<L2>
L1 = { detailId = 5, fileName = "string 1" }{ detailId = 5, fileName = "string 2" }
L2 = { detailId = 5, fileName = "string 2" }{ detailId = 5, fileName = "string 3" }
That I want to combine them with no duplicates:
List<L3>
L1 = { detailId = 5, fileName = "string 1" }{ detailId = 5, fileName = "string 2" }{ detailId = 5, fileName = "string 3" }
I've tried:
L1.Union(L2).ToList();
L1.Concat(L2).Distinct().ToList();
But both return with duplicates (1, 2, 2, 3).
Not sure what I'm missing.
edit Here's the method. It takes one list and creates another from a delimited string, and tries to combine them.
private List<Files> CombineList(int detailId, string fileNames)
{
List<Files> f1 = new List<Files>();
List<Files> f2 = new List<Files>();
f1 = GetFiles(detailId, false);
if (f1[0].fileNames != "")
{
string[] names = fileNames.Split('|');
for (int i = 0; i < names.Length; i++)
{
Files x = new Files();
x.detailId = detailId;
x.fileNames = names[i];
f2.Add(x);
}
List<Files> f3 = f1.Union(f2).ToList();
}
return f3;
}
Upvotes: 3
Views: 2508
Reputation: 46047
If you're merging a list of objects, you will need to define some criteria for the equality comparisons. The example below demonstrates this:
class MyModelTheUniqueIDComparer : IEqualityComparer<MyModel>
{
public bool Equals(MyModel x, MyModel y)
{
return x.SomeValue == y.SomeValue && x.OtherValue == y.OtherValue;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(MyModel myModel)
{
unchecked
{
int hash = 17;
hash = hash * 31 + myModel.SomeValue.GetHashCode();
hash = hash * 31 + myModel.OtherValue.GetHashCode();
return hash;
}
}
}
Then you can call to get the result:
var result = q1.Union(q2, new MyModelTheUniqueIDComparer());
Upvotes: 1
Reputation: 171
I don't like overriding the Files class equals object and getHashCode since you are interfering with the object. Let another object do that and just pass it in. This way if you have an issue with it later on, just swap it out and pass another IEqualityComparer Here's an example you can just test out
public void MainMethod()
{
List<Files> f1 = new List<Files>() { new Files() { detailId = 5, fileName = "string 1" }, new Files() { detailId = 5, fileName = "string 2" } };
List<Files> f2 = new List<Files>() { new Files() { detailId = 5, fileName = "string 2" }, new Files() { detailId = 5, fileName = "string 3" } };
var f3 = f1.Union(f2, new FilesComparer()).ToList();
foreach (var f in f3)
{
Console.WriteLine(f.detailId + " " + f.fileName);
}
}
public class Files
{
public int detailId;
public string fileName;
}
public class FilesComparer : IEqualityComparer<Files>
{
public bool Equals(Files x, Files y)
{
return x.fileName == y.fileName;
}
public int GetHashCode(Files obj)
{
return obj.fileName.GetHashCode();
}
}
Upvotes: 3
Reputation: 8502
From MSDN Enumerable.Union Method:
If you want to compare sequences of objects of a custom data type, you have to implement the IEqualityComparer < T > generic interface in the class.
A sample implementation specific to your Files
class so that the Union
works correctly when merging two collections of custom type:
public class Files : IEquatable<Files>
{
public string fileName { get; set; }
public int detailId { get; set; }
public bool Equals(Files other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
//Check whether the products' properties are equal.
return detailId.Equals(other.detailId) && fileName.Equals(other.fileName);
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
//Get hash code for the fileName field if it is not null.
int hashFileName = fileName == null ? 0 : fileName.GetHashCode();
//Get hash code for the detailId field.
int hashDetailId = detailId.GetHashCode();
//Calculate the hash code for the Files object.
return hashFileName ^ hashDetailId;
}
}
Upvotes: 0
Reputation: 4619
From MSDN, for Union :
The default equality comparer, Default, is used to compare values of the types that implement the IEqualityComparer(Of T) generic interface. To compare a custom data type, you need to implement this interface and provide your own GetHashCode and Equals methods for the type.
Since you use a custom type, you need to either override the GetHashCode
and Equals
or provide an object that implements the IEqualityComparer interface (preferably IEquatable
) and provide it as a second parameter to Union
.
Here's a simple example of implementing such a class.
Upvotes: 6
Reputation: 6608
If your elements do not implement some kind of comparison interface (Object.Equals, IEquatable, IComparable, etc.) then any equality test between them will involve ReferenceEquals
, in which two different objects are two different objects, even if all their members contain identical values.
Upvotes: 1