Joe Lloyd
Joe Lloyd

Reputation: 22453

Comparing 2 lists of objects

I have several classes in my c# application.

I have a plant class with a constructor that takes a name and a weight.

Then I have a Fruit class that inherits plant and adds the number of seeds attribute.

Also I have a Veg class that inherits from plant and adds the savoryLevel attribute.

fruit and veg can be added to their lists by the user.

I have overloaded the == operator in fruit so that it compares the names of fruit and veg and if they have the same name it tells you. My issue is when I try to compare the whole lists to find duplication, I just cant get the code to work at all.

here is some of my code plant class

    public string name;
    public string weight;

    public Plant(string name, string weight)
    {
        this.name = name;
        this.email = weight;
    }
    ....

    public static bool operator ==(Plant a, Plant b)
    {
        // If both are null, or both are same instance, return true.
        if (System.Object.ReferenceEquals(a, b))
        {
            return true;
        }

        // If one is null, but not both, return false.
        if (((object)a == null) || ((object)b == null))
        {
            return false;
        }

        // Return true if the fields match:
        return a.name == b.name;
    }

then the new fruit constructor

 string seeds;

public fruit(string name, string weight, string seeds)
        : base(name, weight)
    {
      this.seeds
    }

here is veg

string savoryLevel;

public fruit(string name, string weight, string savouryLevel)
        : base(name, weight)
    {
      this.savoryLevel
    }

here is the main where I compare 2 instances, this works fine

  Fruit f = new Fruit("apple", "2", "5");
  Veg v = new Veg("carrot", "3", "7");

  if (f == v)
     { 
     Console.WriteLine("They are the same");
     }
      else{
     Console.WriteLine("They are different");
     }

This is the tricky part, I need to iterate through my entire list of veg and fruit and see if any of the fruit have the same name as the veg.

using the lists directly wont work

    List<Fruit> fr = new List<Fruit>();
    List<Veg> ve = new List<Veg>();

    if(fr == ve){
    Console.....
    }
     else{
     Console....
    }

So how do I get the lists to compare and print out some result to say these are the same or these are not the same? Any help is really appreciated, thanks. please just ask if you would like more info.

Upvotes: 0

Views: 656

Answers (6)

Joe Lloyd
Joe Lloyd

Reputation: 22453

First off thanks everyone for the input, You've all helped me see the problem more clearly. But Overloading the == operator is something I had to do as part of the requirements.

I have however found a relatively simple way to compare the 2 lists that uses the overloaded == operator I added to the Plant Class

By nesting a forEach loop I check every list item of veg against every list item of fruit.

public void Compare(List<Fruit> frList, List<Veg> vList)
    {
        foreach (Fruit f in frList)
        {
            foreach (Veg v in vList)
            {
                if (f == v)
                { 
                   //some functionality
                }else{
                  //some other funtionality
                }
            }

        }
    }

This still uses the overloaded == operator in plant and will only compare the name when I call the the method in the main. i.e. even if the plants have different weights they will be considered the same.

Thanks again for the input guys.

Upvotes: 0

John Alexiou
John Alexiou

Reputation: 29264

I think you should use IEquatable<Plant> and cast the lists into List<Plant> with SequenceEquals()

Demo:

public class Plant : IEquatable<Plant>
{
    public string Name { get; set; }
    public string Weight { get; set; }

    public override bool Equals(object obj)
    {
        var other=obj as Plant;
        if(other!=null)
        {
            return Equals(other);
        }
        return false;
    }
    public bool Equals(Plant other)
    {
        Debug.WriteLine("Checking Equality Between {0} And {1}", Name, other.Name);
        return Name.Equals(other.Name);
    }
    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }
}

public class Fruit : Plant
{
    public string Seeds { get; set; }
}

public class Veg : Plant
{
    public string SavoryLevel { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        List<Fruit> fruits=new List<Fruit>() { 
            new Fruit() { Name="apple", Weight = "2", Seeds="5" },
            new Fruit() { Name="banana", Weight="1", Seeds="30" }
        };

        List<Veg> veggies=new List<Veg>() {
            new Veg() { Name = "carrot", Weight="3", SavoryLevel="7" },
            new Veg() { Name = "potato", Weight="5", SavoryLevel="1" }
        };

        var equal=fruits.Cast<Plant>().SequenceEqual(veggies);
        var unique_fruits=fruits.Distinct();
    }
}

It produces the output

Checking Equality Between apple And carrot

and then the equality comparison ends (since it is false). The point being is that it calls the appropriate Equals() function.

Upvotes: 2

Bob.
Bob.

Reputation: 4002

You really don't need to overload the Equals method to find out if something is different. Overloading the Equals method should be used if you are looking for a different behaviour, not for different results.

Since this case compares two string members in two different classes, why not just use LINQ to compare the members themselves which are of the same datatype?

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    public static void Main()
    {
        List<Fruit> f = new List<Fruit>();
        Fruit fTemp = new Fruit() { name = "Kiwi" };
        f.Add(fTemp);
        fTemp = new Fruit() { name = "Tomato" };
        f.Add(fTemp);

        List<Veg> v = new List<Veg>();
        Veg vTemp = new Veg() { name = "Tomato" };
        v.Add(vTemp);

        List<Veg> vDuplicates = v.Where(vegToCompare=>f.Any(fruitToCompare=>fruitToCompare.name.Equals(vegToCompare.name))).ToList();
        vDuplicates.ForEach(a=>Console.WriteLine(a.name));
        Console.WriteLine("Number of Duplicates Found: " + vDuplicates.Count);
    }
}

public class Fruit
{
    public string name;
}

public class Veg
{
    public string name;
}

Upvotes: 1

Matthew Haugen
Matthew Haugen

Reputation: 13286

I'd use LINQ, and rather that (or in addition to) overloading the == operator, go for the "more native" object.Equals and object.GetHashCode.

public override int GetHashCode()
{
    return this.name.GetHashCode();
}

public override bool Equals(object b)
{
    Plant bPlant = b as Plant;
    // If one is null, but not both, return false.
    if (bPlant == null)
    {
        return false;
    }

    // Return true if the fields match:
    return this.name == b.name;
}

Then you can use LINQ:

return fr.SequenceEquals(ve);

Note, of course, that, as the name implies, this only works when fr and ve are exactly equal. That is to say, the order must be the same between them: if both contain "Carrot, Broccoli," you'll be fine, but if one is that and the other is "Broccoli, Carrot," this will return false.

Unless I'm misunderstanding, and in fact you want the intersection, not to know that they're equal, in which case:

return fr.Intersect(ve);

Upvotes: 1

DLeh
DLeh

Reputation: 24395

if you want to to it per item, you could do it like this

foreach(var fruit in fr)
{
    if(ve.Any(x => x.Name == fruit.Name))
    {
        Console.Write(fruit.Name + " is in both lists");
    }
}

Upvotes: 1

Selman Gen&#231;
Selman Gen&#231;

Reputation: 101701

If you wanna compare the items at the same index Zip method can be useful:

bool result = fr.Zip(ve, (f,v) => new { f, v }).All(x => x.f == x.v);

Zip methods create pairs of corresponding items, then put each pair into an anonymous type. And All method simply checks if all items in the pairs are equal.

Upvotes: 1

Related Questions