简体   繁体   中英

c# get difference between two lists that use structs

This is my first post. I'm fairly new in c# and I'm trying to use lists of a struct type.

public struct MyStruct
{
    public string Field1;
    public string Field2;
}

List<MyStruct> BigList = new List<MyStruct> { };
List<MyStruct> SmallList = new List<MyStruct> { }; 

So far, everything I've done used a known type (eg string) but for the lists in the example above, I cannot use any of the functions with a positive result.

At moment, the idea is to get the differences between two lists that use that struct type (List). The BigList will always have more items than the Small list and the idea is to find all items in the SmalList which are not existing in the BigList, considering the Field1. Ideally, the result could be a list of the same type (List).

Is this possible? I've tried some examples I've found in stackoverflow but couldn't find many examples that use lists of structs, and the ones I've tried didn't work.

One example that worked fine with a list of strings:

var Missing = BigList.Except(SmallList).ToList() 

could be a solution, but I need to look at one field (Field1 of the struct) not just a simple string.

Cannot have positive results using .Sort or .Contains for those type of lists (looking specifically to one field of the struct eg List).

Can someone help me? That would be great!

Thanks in advance. Regards,

As other said, the use of the struct in your example, especially since you're new to C# is probably more of a mistake than a real good idea. I suggest you replace your struct with a class.

The issue you're facing is because since you're using a type that you have defined yourself, the comparison won't work, or at least won't work as you expect it to work.

It won't know how to compare two objects and will, in the case of classes, verify if it's the same object and in case of structs either do a byte-to-byte comparison (if the struct doesn't contain reference types) or compare that the references it contains are the same (if the struct contains reference types).

What you need to do to make everything work correctly is to override the Equals method in your class. See this MSDN article to get started on this: https://msdn.microsoft.com/en-us/library/336aedhh(v=vs.100).aspx .

Alternatively, you can implement the IEqualityComparer for your type. It's especially useful if you don't have access to the class implementation. Check this MSDN article to do so: https://msdn.microsoft.com/en-us/library/bb300779(v=vs.110).aspx

There are many ways to solve this problem. Here is a quite expressive way to find elements of BigList that don't have equivalents SmallList using Field1 .

var bigListWithoutSmallList = 
    BigList.Where( b => !SmallList.Any( x => x.Field1 == b.Field1 ));

NOTE: Using a class or struct is not relevant really for your problem.

one solution could be to implement IEquatable<MyStruct>

 class Program
    {
        static void Main(string[] args)
        {
            var smallList = new List<MyStruct>() { new MyStruct
          {
              Field1="1f",
               Field2 ="2f"
          },
          new MyStruct
          {
              Field1="2f",
               Field2 ="22f"
          }};
            var bigList = new List<MyStruct>() { new MyStruct
          {
              Field1="1f",
               Field2 ="2f"
          },
          new MyStruct
          {
              Field1="3 f",
               Field2 ="22f"
          },new MyStruct
          {
              Field1="4f",
               Field2 ="22f"
          } 
            };


              //find the difference 
            var diffList = smallList.Except(bigList);  

        }
        public struct MyStruct:IEquatable<MyStruct>
        {
            public string Field1;
            public string Field2;

            public bool Equals(MyStruct other)
            {
                if (this.Field1==other.Field1 && this.Field2==other.Field2)
                {
                    return true;   
                }
                return false;   
            }
            public override int GetHashCode()
            {
                return Field1.GetHashCode() & Field2.GetHashCode();
            }
       public override bool Equals(object obj)
        {
            return  this.Equals(obj);
        }
        }
    }

The result could be the struct with

       new MyStruct
              {
                  Field1="2f",
                   Field2 ="22f"
              }};

There is an open source library called MoreLINQ that provides an extension method called ExceptBy for IEnumerable<T> that can do what you need.

ExceptBy has the following signature:

public static IEnumerable<TSource> ExceptBy<TSource, TKey>(
    this IEnumerable<TSource> first, 
    IEnumerable<TSource> second, 
    Func<TSource, TKey> keySelector);

The key selector is a function that maps source values to key values which are used for the comparison, but the result is still a sequence of TSource .

In your case it would be invoked like this:

var Missing = BigList
    .ExceptBy(SmallList, (item => item.fieldToCompareBy)
    .ToList();

This method has pretty good performance compared to using Contains or Any inside a Where clause. It only has to iterate through the second sequence once immediately, and streams results from the first sequence lazily.

Except will work if you just override Equals in the struct. The trick is you need to treat it as Nullable internally.

class Program
{
    static void Main(string[] args)
    {
        var smallList = new List<MyStruct>()
        {
            new MyStruct { Field1="1f", Field2 ="2f" },
            new MyStruct { Field1="2f", Field2 ="22f" }
        };

        var bigList = new List<MyStruct>()
        {
            new MyStruct { Field1="1f", Field2 ="2f" },
            new MyStruct { Field1="3f", Field2 ="22f" },
            new MyStruct { Field1="4f", Field2 ="22f" }
        };

        var result = bigList.Except(smallList);

        Console.ReadLine();
    }
}

public struct MyStruct
{
    public string Field1;
    public string Field2;
    public override bool Equals(object obj)
    {
        return Field1 == (obj as Nullable<MyStruct>).Value.Field1;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM