1

I would like to know how to achieve grouping and eliminating repeated data, for example I have this class:

public class MyObject
{
    public string attrb1{ get; set; }
    public string attrb2{ get; set; }
}

and I want to "debug" the repeated objects, try the following, but it does not work.

List<MyObject> ListObject = new List<MyObject>();

var obj1 = new MyObject{attrb1="attrb1", attrb2="attrb2"}
ListObject.Add(obj1);

var obj2 = new MyObject{attrb1="attrb1", attrb2="attrb2"}
ListObject.Add(obj2);


List<MyObject> GroupedList= new List<MyObject>();

foreach(var obj in ListObject)
{
    if(!GroupedList.Contains(obj))
        GroupedList.Add(obj);
}

4 Answers4

1

By default, equality between classes is determined by comparing their reference (memory location), so two classes are only equal if they both point to the same instance.

If you want to define a different way of determining equality, then you need to override the Equals method (and GetHashCode).

It appears that you want to consider two MyObject instances equal if both their attrib1 properties are equal and their attrib2 properties are equal. If that's the case, you can override Equals like so:

public class MyObject
{
    public string attrb1 { get; set; }
    public string attrb2 { get; set; }

    public override bool Equals(object obj)
    {
        var other = obj as MyObject;
        return other != null && other.attrb1 == attrb1 && other.attrb2 == attrb2;
    }

    public override int GetHashCode()
    {
        return (attrb1 + attrb2).GetHashCode();
    }
}

After this change, the GroupedList in your sample code will only contain one item.

Rufus L
  • 36,127
  • 5
  • 30
  • 43
1

What you're looking for is .Distinct(). To use it for custom objects, you would first need to define an EqualityComparer, like so:

public class MyObjectComparer : EqualityComparer<MyObject>
{
    public override bool Equals(MyObject x, MyObject y)
    {
        return x.attrb1 == y.attrb1 && x.attrb2 == y.attrb2;
    }

    public override int GetHashCode(MyObject obj)
    {
        return (obj.attrb1 + obj.attrb2).GetHashCode();
    }
}

Then you can use it as (thus eliminating that for loop entirely):

var distinct = ListObject.Distinct(new MyObjectComparer());
Seva
  • 1,631
  • 2
  • 18
  • 23
0

You might want to check out this link, and the following examples in particular:

int[] ints1 = { 5, 3, 9, 7, 5, 9, 3, 7 };
int[] ints2 = { 8, 3, 6, 4, 4, 9, 1, 0 };

IEnumerable<int> union = ints1.Union(ints2);

and this

List<int> ages = new List<int> { 21, 46, 46, 55, 17, 21, 55, 55 };

IEnumerable<int> distinctAges = ages.Distinct();

Just make sure you are importing System.Linq

using System.Linq
agfc
  • 852
  • 1
  • 6
  • 13
  • But `Union` and `Distinct` won't work with a `MyObject` as the OP wants since they use the `Equals` method to determine equality... – Rufus L Mar 29 '19 at 14:10
  • They don't have to use it. I don't see it in their code. They can write a comparer as suggested by other answers. But using Distinct or Union is a way to go if you want to select unique values from a collection " eliminating repeated data" as they put it. – agfc Mar 29 '19 at 14:16
  • 1
    I meant that this answer appears to be suggesting that they modify their sample code to: `IEnumerable GroupedList = ListObject.Distinct();`, and I was saying that this won't work as expected without overriding `Equals` or writing a comparer. – Rufus L Mar 29 '19 at 14:28
0

The best solution would be use Enumerable.Union that will return a new list with the union of both sets. To do it, we will need to override Object.Equals to be sure that only the properties will be compared and not references

To make it easier, we will use IEquatable<T>

/// <summary>
/// Inherit from interface <see cref="IEquatable{T}"/>
/// </summary>
public class Objeto : IEquatable<Objeto>
{
    public string atributo1 { get; set; }
    public string atributo2 { get; set; }

    /// <summary>
    /// Implements <see cref="Equals(Objeto)"/> method from interface <see cref="IEquatable{T}"/>
    /// </summary>
    /// <param name="other">The second object we will compare.</param>
    /// <returns></returns>
    public bool Equals(Objeto other)
    {
        //If the object is null, are not equal.
        if(other==null)return false;
        //If isn't null we compare both attributes.
        return atributo1==other.atributo1&&atributo2==other.atributo2;
    }

    //Override Equals calling the Equals(Object) implementation.
    public override bool Equals(object obj) => Equals(obj as Objeto);

    //override GetHashCode making sure that if Equals is true, both objects must have the same HashCode.
    public override int GetHashCode()
    {
        return atributo1.GetHashCode() ^ atributo2.GetHashCode();
    }
}

Now, we can use Enumerable.Union with objects of type Objeto

List<Objeto> list1 = new List<Objeto>{new Objeto{atributo1="Dato1", atributo2 = "Dato2"}};
List<Objeto> list2 = new List<Objeto>
{
    new Objeto{atributo1="Dato1", atributo2 = "Dato2"},
    new Objeto{atributo1="Dato3", atributo2 = "Dato4"}
};

//Get the unión between both lists
var listaDefinitiva = list1.Union(list2);

foreach (var objeto in listaDefinitiva)
{
    Console.WriteLine("Atributo1: {0}\t Atributo2: {1}",objeto.atributo1,objeto.atributo2);
}

The console output will be

Atributo1: Dato1         Atributo2: Dato2
Atributo1: Dato3         Atributo2: Dato4
Juan Salvador Portugal
  • 1,233
  • 4
  • 20
  • 38