如何区分两个列表并获得净更改

时间:2012-12-17 23:45:22

标签: c# algorithm list

我有一个对象和两个列表如下:

    public class MyObject
    {
        public int Key;
        public DateTime Day;
        public decimal Value;
    }        

    List<MyObject> listA = new List<MyObject>()
    {
        new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 8 },
        new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
        new MyObject() { Key = 2, Day = new DateTime(2012, 12, 17), Value = 8 },
        new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 4 },
        new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 }
    };

    List<MyObject> listB = new List<MyObject>()
    {
        new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 2 },
        new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
        new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 8 },
        new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 },
        new MyObject() { Key = 5, Day = new DateTime(2012, 12, 17), Value = 10 }
    };

我正在寻找的结果是:

    List<MyObject> listChanges = new List<MyObject>()
    {
        new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = -6 },
        new MyObject() { Key = 2, Day = new DateTime(2012, 12, 17), Value = -8 },
        new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 4 },
        new MyObject() { Key = 5, Day = new DateTime(2012, 12, 17), Value = 10 }
    };

基本上,我正在尝试创建一个列表,其中包含将listA转换为listB所需的更改。因此,虽然可以使用Except和来自LINQ的Intersect,但我认为他们没有最好的性能来完成这样的任务,因为你仍然需要另外的比较以获得值的差异。

我有一个想法是:如果我遍历listA,我可以从listA和listB中删除该项(如果找到它,此时我可以确定+/-差异)。完成listA后,listB只包含添加内容。

如何获得更改结果?

3 个答案:

答案 0 :(得分:0)

怎么样:

        List<MyObject> listA = new List<MyObject>(){
            new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 8 },
            new MyObject() { Key = 2, Day = new DateTime(2012, 12, 17), Value = 8 },
            new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
            new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 },
            new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 4 }
        };

        List<MyObject> listB = new List<MyObject>(){
            new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 2 },
            new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 8 },
            new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 },
            new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
            new MyObject() { Key = 5, Day = new DateTime(2012, 12, 17), Value = 10 }
        };
        List<MyObject> listChanges = Comparer(listA, listB);
        MyObject[] hasil = listChanges.ToArray();
        for (int a = 0; a < hasil.Length;a++ ) {
            Console.WriteLine(hasil[a].Key+" "+hasil[a].Day+" "+hasil[a].Value);
        }

和功能:

    private MyObject[] sort(List<MyObject> input) {
        //sort input with it's key
        MyObject[] gg = input.ToArray();
        for (int a = 0; a < input.Count; a++) {
            for (int b = a + 1; b < input.Count; b++) {
                if (gg[a].Key > gg[b].Key) {
                    MyObject temp = gg[a];
                    gg[a] = gg[b];
                    gg[b] = temp;
                }
            }
        }
        //sort input, if key is same => sort the date
        for (int a = 0; a < input.Count; a++) {
            int indStart = a;
            int indEnd = a;
            for (int b = a + 1; b < input.Count; b++) {
                if (gg[a].Key == gg[b].Key) {
                    indEnd++;
                } else {
                    b = input.Count;
                }
            }
            a = indEnd;
            for (int c = indStart; c <= indEnd; c++) {
                for (int d = c + 1; d <= indEnd; d++) {
                    if (gg[c].Day > gg[d].Day) {
                        MyObject temp = gg[c];
                        gg[c] = gg[d];
                        gg[d] = temp;
                    }
                }
            }
        }
        return gg;
    }
    private List<MyObject> Comparer(List<MyObject> listA, List<MyObject> listB) {
        List<MyObject> output = new List<MyObject>();
        //if you sure that the list was sorted, u just remove the sort function
        MyObject[] ff = sort(listA);
        MyObject[] gg = sort(listB);
        Boolean[] masuk = new Boolean[gg.Length];
        //foreach element in listA, search the changes in input
        for (int a = 0; a < listA.Count;a++ ) {
            //find element in input which contains the changes of element in listA
            Boolean ins = false;
            for (int b = 0; b < listB.Count;b++ ) {
                if (masuk[b])
                    continue;
                if (ff[a].Key >= gg[b].Key) {
                    if (ff[a].Key == gg[b].Key && ff[a].Day == gg[b].Day){
                        masuk[b] = true;
                        if (gg[b].Value != ff[a].Value) {
                            output.Add(new MyObject() { Key = gg[b].Key, Day = gg[b].Day, Value = gg[b].Value - ff[a].Value });
                            b = listB.Count;
                        }
                        ins = true;
                    }
                } else {
                    b = listB.Count;
                }
            }
            if (!ins) {
                output.Add(new MyObject() { Key = ff[a].Key, Day = ff[a].Day, Value = -ff[a].Value });
            }
        }
        for (int a = 0; a < gg.Length;a++ ) {
            if(!masuk[a]){
                output.Add(new MyObject() { Key = gg[a].Key, Day = gg[a].Day, Value = gg[a].Value });
            }
        }
        return output;
    }

和输出:

    1 12/17/2012 12:00:00 AM -6
    2 12/17/2012 12:00:00 AM -8
    3 12/17/2012 12:00:00 AM 4
    5 12/17/2012 12:00:00 AM 10

答案 1 :(得分:0)

首先,我将实现一个基于IEqualityComparer<T>Key属性检查相等性的Day。然后你可以使用linq如下:

var notInA = listB.Except(listA, myEqualityComparer);
var notInB = listA.Except(listB, myEqualityComparer)
                   .Select(o => {
                      return new MyObject {
                         Key = item.Key,
                         Day = item.Day,
                         Value = item.Value * -1
                      };
                   });
var listA2 = listA.Intersect(listB, myEqualityComparer)
                  .OrderBy(o => o.Key)
                  .ThenBy(o => o.Day);
var listB2 = listB.Intersect(listA, myEqualityComparer)
                  .OrderBy(o => o.Key)
                  .ThenBy(o => o.Day);
var diff = listA2.Zip(listB2, (first,second) => {
   return new MyObject {
     Key = first.Key,
     Day = first.Day,
     Value = second.Value - first.Value
});
diff = diff.Concat(notInA).Concat(notInB);

答案 2 :(得分:0)

这应该这样做。如果您的任何Key / Day组合在您的任何一个输入中都不是唯一的,那么它会抛出异常。

public static IEnumerable<MyObject> GetChanges(
    IEnumerable<MyObject> from, IEnumerable<MyObject> to)
{
    var dict = to.ToDictionary(mo => new { mo.Key, mo.Day });

    // Check that keys are distinct in from, too:
    var throwaway = from.ToDictionary(mo => new { mo.Key, mo.Day });

    // Adjustments of items found in "from"
    foreach (MyObject mo in from)
    {
        var key = new { mo.Key, mo.Day };
        MyObject newVal;
        if (dict.TryGetValue(key, out newVal))
        {
            // Return item indicating adjustment
            yield return new MyObject { 
                Key = mo.Key, Day = mo.Day, Value = newVal.Value - mo.Value };
            dict.Remove(key);
        }
        else
        {
            // Return item indicating removal
            yield return new MyObject {
                Key = mo.Key, Day = mo.Day, Value = -mo.Value };
        }
    }

    // Creation of new items found in "to"
    foreach (MyObject mo in dict.Values)
    {
        // Return item indicating addition
        // (Clone as all our other yields are new objects)
        yield return new MyObject {
            Key = mo.Key, Day = mo.Day, Value = mo.Value };
    }
}

可以通过移除from上的唯一性检查或即时执行(尝试将每个项目的关键部分添加到HashSet)来加快速度,但我认为你不能避免循环部分to两次 - 一次构建字典,一次返回余数。