Reputation: 461
I have a class
class Foo{
string valueOne;
string valueTwo;
}
I have multiple of objects of Foo in a List. These objects are already distinct. However, I want to also remove objects if there are another object with equal valueOne and valueTwo == null. It is guaranteed that there will always only be maximum one other "duplicate" with the same value in valueOne. There can be one or more objects with the same valueOne, but different value in valueTwo.
Eg:
var foo1 = new Foo{
valueOne = "Equal Value",
valueTwo = "Some value"
};
var foo2 = new Foo{
valueOne = "Equal Value",
valueTwo = "Different value"
};
var foo3 = new Foo{
valueOne = "Equal Value",
valueTwo = null
};
var foo4 = new Foo{
valueOne = "SomeOtherValue",
valueTwo = "whatever"
};
var foo5 = new Foo{
valueOne = "I have null value, but should still be in list",
valueTwo = null
};
And after removing the duplicates with null values, I should have:
foo1:
valueOne = "Equal Value"
valueTwo = "Different"
foo2:
valueOne = "Equal Value"
valueTwo = "Different value"
foo4:
valueOne = "SomeOther Value"
valueTwo = "whatever"
foo5
valueOne = "I have null value, but should still be in list"
valueTwo = null
So basically: A Foo object can stay in the list with valueTwo == null, only if there isn't another object with same valueOne.
I made it work by iterating over the list with a nested loop, and storing the indexes of the duplicates, and then looping over the indexes and using RemoveAt(index) on the list. However, this is really long and ugly code. Is there a way to accomplish this with Linq? I'm new to C# and Linq, hope this works though!
Thanks for any help!
EDIT: Just to make it clearer, I already call GroupBy on the list, making it distinct.
EDIT 2: Damnit, I wasn't clear in my explanation. There can be one or more objects with the same valueOne, but different value in valueTwo. It is NOT guaranteed that there will always only be maximum one other "duplicate" with the same value in valueOne.
It is although guaranteed that there will for every Foo object with a distinct valueOne, only be one other Foo object with the same valueOne, but with null as valueTwo. If that makes sense..
Upvotes: 0
Views: 177
Reputation: 2732
It sounds like you may want your Foo class to implement IEquatable<T>
, add in your custom comparison logic and then you can use .Distinct on it, as you like. You can find out more info about it here
Updated: Here is an implementation using a custom IEquatable. You should be able to tweak the logic to suit your needs if I am missing anything from your edge cases.
Upvotes: 1
Reputation: 1369
A custom comparer by itself won't work. But using group by with distinct will. Start with a comparer that will compare by value:
class FooCompare : IEqualityComparer<Foo>
{
public bool Equals([AllowNull] Foo x, [AllowNull] Foo y)
{
if (null == x && null == y)
return true;
if (null == x || null == y)
return false;
return x.ValueOne == y.ValueOne && x.ValueTwo == y.ValueTwo;
}
public int GetHashCode([DisallowNull] Foo obj)
{
return HashCode.Combine(obj.ValueOne, obj.ValueTwo);
}
}
Here's a console main with the test data to show how it works.
static void Main(string[] _)
{
var data = new Foo[] {
new Foo { ValueOne = "Equal Value", ValueTwo = null },
new Foo { ValueOne = "Equal Value", ValueTwo = "Different" },
new Foo { ValueOne = "Equal Value", ValueTwo = "not same" },
new Foo { ValueOne = "SomeOtherValue", ValueTwo = "Different" },
new Foo { ValueOne = "SomeOtherValue", ValueTwo = null },
new Foo { ValueOne = "I have null value, but should still be in list",
ValueTwo = null },
};
var check = new Foo[] {
new Foo { ValueOne = "Equal Value", ValueTwo = "Different" },
new Foo { ValueOne = "Equal Value", ValueTwo = "not same" },
new Foo { ValueOne = "SomeOtherValue", ValueTwo = "Different" },
new Foo { ValueOne = "I have null value, but should still be in list",
ValueTwo = null },
}.OrderBy(x => x.ValueOne);
var compare = new FooCompare();
var result2 = data.GroupBy(x => x.ValueOne)
.SelectMany(g =>
{
var d = g.Distinct(compare);
var nonNull = d.Where(x => x.ValueTwo != null);
if (nonNull.Any())
return nonNull;
return d;
})
.OrderBy(x => x.ValueOne)
.ToArray();
if (!Enumerable.SequenceEqual(check, result2, new FooCompare()))
Console.WriteLine("Failed");
else
Console.WriteLine("Success");
// this one will not work.
var result = data.GroupBy(x => x.ValueOne)
.Select(g => g.OrderByDescending(y => y.ValueTwo).First())
.ToArray();
if (!Enumerable.SequenceEqual(check, result, new FooCompare()))
Console.WriteLine("Failed");
else
Console.WriteLine("Success");
}
Also included the original GroupBy that will not work.
Upvotes: 1