基于条件从C#中的List<T>中删除重复项

3
我有一个对象列表(在下面的代码示例中为exobject),每个对象都有SomeId、AnotherId、SomeOtherId和Timestamp。这个列表可能有重复条目,每个记录的时间戳不同。我想删除所有具有较旧时间戳的此对象的重复项,仅保留最新的那些。
示例对象:
SomeId    AnotherId    SomeOtherId    Timestamp
1         2            1              10
1         2            1              20
1         3            2              30
2         3            4              40
1         3            2              50

我的需求清单应该是:
1,2,1,20 and 1,3,2,50 and 2,3,4,40.

我有一个使用C#非常粗糙的实现来做这件事。
for (int i = 0; i < exObject.Count - 1; i++)
{
    for (int j = i + 1; j < exObject.Count - 1; j++)
    {
        if (exObject[i].SomeId == exObject[j].SomeId && exObject[i].AnotherId == exObject[j].AnotherId && exObject[i].SomeOtherId == exObject[j].SomeOtherId)
         {
             if (exObject[i].TimeStamp < exObject[j].TimeStamp)
                 exObject[i].TimeStamp = exObject[j].TimeStamp;
             exObject.Remove(exObject[j]);
         }
    }
}

我想知道是否有更优雅、更好的方法来做这件事,或者是否有一个lambda可以用来完成这个任务。

你不想使用 exObject.Distinct() 吗? - Mehdi
我现在正在尝试使用Distinct方法。 - yazz
2个回答

3
System.Linq有一个Distinct方法。您需要实现IEqualityComparer接口来使用它。详情请查看此链接:https://msdn.microsoft.com/zh-cn/library/bb338049(v=vs.110).aspx 如果您按照orderBy方法排序,您可以保留您想要的元素,以下是示例代码:
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var data = new[]
            {
                new SomeClass { SomeId = 1, AnotherId = 1, SomeOtherId = 1, Timestamp = 10 },
                new SomeClass { SomeId = 1, AnotherId = 1, SomeOtherId = 1, Timestamp = 20 }, // Duplicate
                new SomeClass { SomeId = 1, AnotherId = 2, SomeOtherId = 2, Timestamp = 30 },
                new SomeClass { SomeId = 1, AnotherId = 2, SomeOtherId = 2, Timestamp = 35 }, // Duplicate
                new SomeClass { SomeId = 2, AnotherId = 4, SomeOtherId = 4, Timestamp = 40 },
                new SomeClass { SomeId = 3, AnotherId = 2, SomeOtherId = 2, Timestamp = 50 },
                new SomeClass { SomeId = 1, AnotherId = 1, SomeOtherId = 1, Timestamp = 50 } // Duplicate
            };

            var distinctList = data
                        .OrderBy(x => x.Timestamp)
                        .Distinct(new SomeClassComparer())
                        .ToList();
            }

        public class SomeClass
        {
            public int SomeId { get; set; }
            public int AnotherId { get; set; }
            public int SomeOtherId { get; set; }
            public int Timestamp { get; set; }
        }

        public class SomeClassComparer : IEqualityComparer<SomeClass>
        {
            public bool Equals(SomeClass x, SomeClass y)
            {
                if (ReferenceEquals(x, y))
                {
                    return true;
                }

                //Check whether any of the compared objects is null.
                if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
                {
                    return false;
                }

                //Check whether the SomeClass's properties are equal.
                return x.SomeId == y.SomeId &&
                       x.AnotherId == y.AnotherId &&
                       x.SomeOtherId == y.SomeOtherId;
            }

            public int GetHashCode(SomeClass someClass)
            {
                //Check whether the object is null
                if (ReferenceEquals(someClass, null))
                {
                    return 0;
                }

                //Get hash code for the fields
                var hashSomeId = someClass.SomeId.GetHashCode();
                var hashAnotherId = someClass.AnotherId.GetHashCode();
                var hashSomeOtherId = someClass.SomeOtherId.GetHashCode();

                //Calculate the hash code for the SomeClass.
                return (hashSomeId ^ hashAnotherId) ^ hashSomeOtherId;
            }
        }
    }
}

我尝试实现它,但是如何在时间戳上添加条件?让它删除具有旧时间戳的记录? - yazz
这与我目前的工作实现类似。非常感谢。 - yazz

1
您可以按照这3个字段进行分组,并取每个组的第一个:
List
  .GroupBy(x=> new {x.prop1, x.prop2, x.prop3 })
  .Select(g=> g.OrderByDescending(o=> o.dateprop).First())
  .ToList();

完美运行的示例:

static void Main(string[] args)
{
    List<Foo> myList = new List<Foo>();
    myList.Add(new Foo(1, 2, 1, 10));
    myList.Add(new Foo(1, 2, 1, 20));
    myList.Add(new Foo(1, 3, 2, 30));
    myList.Add(new Foo(2, 3, 4, 40));
    myList.Add(new Foo(1, 3, 2, 50));

    // The following returns 3 results with 20, 50 and 40 timeStamps.

    var results = myList.GroupBy(x => new { x.SomeId, x.AnotherId, x.SomeOtherId })
                            .Select(g => g.OrderByDescending(o => o.Timestamp).First()).ToList();

}

第一条记录将是记录的第一个出现。但它可能不是时间戳最新的记录。即具有较旧时间戳的记录可能会在列表后面出现。我对lambda不熟悉 - 是否有GroupBy和OrderByDesc的组合?请提供一个代码片段。 - yazz
然后你可以按日期字段排序,然后取第一个。 - Zein Makki
这总是只返回给我第一条记录。 - yazz
我已经添加了一个示例,它完全按照你的要求工作。请查看一下。 - Zein Makki
我会再试一次。谢谢你提供的代码片段。 - yazz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接