简体   繁体   中英

Getting the exact diff from the Ienumerable.except

I am using LINQ ENumerable.except to get the diff out of two lists of MyType. When I find a diff, I need to retrieve what was different in both base and compare list for the corresponding diff item. LIke,

I have two lists to compare

Base:
Name="1",Type="a",Switch="384347324372432"
Name="1",Type="b",Switch="43432432423"
Name="2",Type="q",Switch="4324324234"

Compare List:
Name="1",Type="a",Switch="aAAAA384347324372432"
Name="1",Type="c",Switch="23432432"
Name="2",Type="q",Switch="3423432423432"
Name="2",Type="q",Switch="4324324234"

Ouuput would be

  Found diff.
  Base: 
  Name="1",Type="a",Switch="384347324372432"
  Corresponding compare value:
  Name="1",Type="a",Switch="aAAAA384347324372432"

  etc...

I have written my own class object, say MyType for storing these as items properties

Defined a custom comparer like

class mycomparer: System.Collections.Generic.IEqualityComparer<MyType>
{

    public mycomparer() {}

    public bool Equals(MyType type1, MyType type2)
    {

        return ( (type1.Name.ToLower() == type2.Name.ToLower()) && (type1.Type.ToLower() ==         type2.Type.ToLower()) (type1.Switch.ToLower() == type2.Switch.ToLower())

    }

    public int GetHashCode(MyType type)
    {

        return string.concat(type.Name, type.Type, type.Switch).ToLower().GetHashCode();

    }

}

In my code, I use

MyComparer mine = new MyComparer();
IEnumerable<MyType> diff = baseList.Except(compareList,mine);

I get the diff correctly. But for the values that are different in baselist, I want to know what the corresponding value in comparelist was.

I do

foreach(Mytype type in diff)
{
     Console.writeline(type.Name+type.Type+type.Switch);
}

How to I get the corresponding values for compare list.

I tried something like,

IEnumerable<MyType> compareValue = Comparelist.Where(tempFile => tempFile.Name.ToLower() ==     type.Name.ToLower() && tempFile.Type.ToLower() == process.Type.ToLower()

(Basically, switch is what could be different here mostly)

But the problem is, in some cases, there may be duplicate Name="" Type="", so the above where retrieves more than one item, so the corresponding comparelist value would be different.

I tried writing the diff values from Equal methods, but didnt work for me.

UPdate:

In the case when duplicate name and type are found and switch has a mismatch, I think diff is calculated correctly, but when the output is written to console, incorrect diff value is written, here is the sample.

   Base:
   Name="2",Type="q",Switch="4324324234"
   Name="2",Type="q",Switch="3423432423432"

   Compare List:
   Name="2",Type="q",Switch="4324324234"
   Name="2",Type="q",Switch="3423432423432dwqdwqdwqdqwdwdq"

   base.except(compare) (as used in my sample code or Moho's solution) gets the diff correctly,

but with diffSwitches.ToList().ForEach

I will get something like

      base:  Name="2",Type="q",Switch="3423432423432"
      Compare:    Name="2",Type="q",Switch="4324324234"

You see that is not the corresponding mismatch. Infact Switch="4324324234" is correctly matched. This is the problem I see.

Thanks.

Try this

baseList.Except( compareList, mine ).Union( compareList.Except( baseList.Except, mine ) )

Edit after new requirements:

        var baseList = new List<MyType>()
        {
            new MyType() { Name="1",Type="a",Switch="384347324372432" },
            new MyType() { Name="1",Type="b",Switch="43432432423" },
            new MyType() { Name="2",Type="q",Switch="4324324234" }
        };

        var compareList = new List<MyType>()
        {
            new MyType() { Name="1",Type="a",Switch="aAAAA384347324372432" },
            new MyType() { Name="1",Type="c",Switch="23432432" },
            new MyType() { Name="2",Type="q",Switch="3423432423432" },
            new MyType() { Name="2",Type="q",Switch="4324324234" }
        };

        // matched Name/Type w/ different Switch
        var diffSwitches = baseList.Join( compareList, 
            bl => new { bl.Name, bl.Type }, 
            cl => new { cl.Name, cl.Type }, 
            ( bl, cl ) => new { 
                baseItem = bl, 
                compareItem = cl } )
            .Where( o => o.baseItem.Switch != o.compareItem.Switch );

        diffSwitches.ToList().ForEach(i => Console.WriteLine("{0}-{1}: {2}; {3}", i.baseItem.Name, i.baseItem.Type, i.baseItem.Switch, i.compareItem.Switch));

Let both list be named list and list2 . This is a solution:

var common = list1.Intersect(list2, new mycomparer());
var result = list1.Concat(list2)
                  .Except(common, new mycomparer())
                  .GroupBy (x => new { x.Name, x.Type } );

So both lists are concatenated, then the elements common in both lists are removed, and finally the remaining items are grouped by Name and Type .

Maybe useful to add that if MyType is a struct , you don't need the comparer, because structs are equal when their values are equal.

I think you can get the results you want with a slight change to your comparer:

    class mycomparer : System.Collections.Generic.IEqualityComparer<Item>
    {

        bool customcomparer = false;
        public mycomparer(bool custom = false) 
        {
            customcomparer = custom;
        }


        public bool Equals(Item type1, Item type2)
        {

            return GetHashCode(type1) == GetHashCode(type2);
        }
        public int GetHashCode(Item type)
        {
            if (customcomparer)
                return string.Concat(type.Name, type.Type).ToLower().GetHashCode();
            return string.Concat(type.Name, type.Type, type.Switch).ToLower().GetHashCode();
        }
    }

And your class set up like this:

    public class MyType
    {
        public String Name, Type, Switch;
        public MyType()
        {

        }
        public MyType(string _name, string _type, string _switch)
        {
            Name = _name;
            Type = _type;
            Switch = _switch;
        }
        public override string ToString()
        {
            return "Name = " + this.Name + ",Type = " + this.Type + ",Switch = " + this.Switch;
        }

    }

And a second IEnumerable like this:

    private void button3_Click(object sender, EventArgs e)
    {
        mycomparer mine = new mycomparer();
        mycomparer mine2 = new mycomparer(true);
        IEnumerable<MyType> diff = baseList.Except(compareList, mine);
        IEnumerable<MyType> diff2 = compareList.Intersect(diff, mine2);
        string message = "Base:\n";
        foreach (MyType item in diff)
        {

            message += item.ToString() + "\n";
        }
        message += "Corresponding compare value:\n";
        foreach (MyType item in diff2)
        {
            message += item.ToString() + "\n";
        }
        MessageBox.Show(message);
    }

I noticed diff returns 2 items Name 1 Type a and Name 1 Type b. This is from your original code so I left it like that.

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