从列表中删除重复项同时保留最新的 C#

Removing Duplicates From a list while keeping the most recent C#

所以我有几个标签类型的列表。标签有几个属性:.Epc、.AntennaPortNumber 和 .LastTimeSeen。我想从主列表中取出所有标签,并将它们分类为 5 个较小的列表,但每个 .Epc 只保留一份副本,并且它必须是最新的 .LastTimeSeen。我什至不确定从哪里开始。我在想我一定需要对嵌套几次的 .GroupBy 做些什么?现在代码分为两个主要部分:我将新标签添加到它们所属的列表中,以及我删除一个 500 毫秒内未出现的标签。我试图用 .Contains 防止将重复的标签添加到 5 个子列表中,但是因为它们具有不同的 LastTimeSeen 属性,所以没有达到预期的效果。

感谢任何人可能提供的任何指导!

List<Tag> tags = new List<Tag>();
    List<Tag> listMed1 = new List<Tag>();
    List<Tag> listMed2 = new List<Tag>();
    List<Tag> listMed3 = new List<Tag>();
    List<Tag> listMed4 = new List<Tag>();
    List<Tag> listMed5 = new List<Tag>();
    void OnTagsReported(ImpinjReader sender, TagReport report)
    {
        // This event handler is called asynchronously 
        // when tag reports are available.
        // Loop through each tag in the report 
        // and print the data.



        //List<Tag> tags = new List<Tag>();
        foreach (Tag tag in report)
        {

            ushort AntennaNum = tag.AntennaPortNumber;


            Impinj.OctaneSdk.TagData first = tag.Epc;

            string epcCheck = first.ToString().ToUpper();
            Impinj.OctaneSdk.ImpinjTimestamp tim = tag.LastSeenTime;

            if (epcCheck.IndexOf("A") != -1)
            {

                if (listMed1.Contains(tag) == false)
                {
                    listMed1.Add(tag);
                    System. Diagnostics.Debug.WriteLine(epcCheck);
                    System.Diagnostics.Debug.WriteLine(tim);
                }
            }

            else if (epcCheck.IndexOf("B") != -1)
            {
                if (listMed2.Contains(tag) == false)
                {
                    listMed2.Add(tag);
                }
            }
            else if (epcCheck.IndexOf("C") != -1)
            {
                if (listMed3.Contains(tag) == false)
                {
                    listMed3.Add(tag);
                }
            }
            else if (epcCheck.IndexOf("D") != -1)
            {
                if (listMed1.Contains(tag) == false)
                {
                    listMed4.Add(tag);
                }
            }
            else if (epcCheck.IndexOf("E") != -1)
            {
                if (listMed5.Contains(tag) == false)
                {
                    listMed5.Add(tag);
                }
            }



            int Med1num = listMed1.Count();
            int Med2num = listMed2.Count();
            int Med3num = listMed3.Count();
            int Med4num = listMed4.Count();
            int Med5num = listMed5.Count();

            for (int loopr = 0; loopr < Med1num; loopr++)
            {
                Impinj.OctaneSdk.ImpinjTimestamp second = listMed1[loopr].LastSeenTime;
                string milisecondsUTC = second.ToString();
                long lastseen = Convert.ToInt64(milisecondsUTC);
                TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
                long secondsSinceEpoch = (long)t.TotalMilliseconds;
                if (secondsSinceEpoch - lastseen > 500 )
                {
                    listMed1.RemoveAt(loopr);
                    loopr = -1;
                }

            }
            for (int loopr = 0; loopr < Med2num; loopr++)
            {
                Impinj.OctaneSdk.ImpinjTimestamp second = listMed2[loopr].LastSeenTime;
                string milisecondsUTC = second.ToString();
                long lastseen = Convert.ToInt64(milisecondsUTC);
                TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
                long secondsSinceEpoch = (long)t.TotalMilliseconds;
                if (secondsSinceEpoch - lastseen > 500)
                {
                    listMed2.RemoveAt(loopr);
                    loopr = -1;
                }

            }
            for (int loopr = 0; loopr < Med3num; loopr++)
            {
                Impinj.OctaneSdk.ImpinjTimestamp second = listMed3[loopr].LastSeenTime;
                string milisecondsUTC = second.ToString();
                long lastseen = Convert.ToInt64(milisecondsUTC);
                TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
                long secondsSinceEpoch = (long)t.TotalMilliseconds;
                if (secondsSinceEpoch - lastseen > 500)
                {
                    listMed3.RemoveAt(loopr);
                    loopr = -1;
                }

            }
            for (int loopr = 0; loopr < Med4num; loopr++)
            {
                Impinj.OctaneSdk.ImpinjTimestamp second = listMed4[loopr].LastSeenTime;
                string milisecondsUTC = second.ToString();
                long lastseen = Convert.ToInt64(milisecondsUTC);
                TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
                long secondsSinceEpoch = (long)t.TotalMilliseconds;
                if (secondsSinceEpoch - lastseen > 500)
                {
                    listMed4.RemoveAt(loopr);
                    loopr = -1;
                }

            }
            for (int loopr = 0; loopr < Med5num; loopr++)
            {
                Impinj.OctaneSdk.ImpinjTimestamp second = listMed5[loopr].LastSeenTime;
                string milisecondsUTC = second.ToString();
                long lastseen = Convert.ToInt64(milisecondsUTC);
                TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
                long secondsSinceEpoch = (long)t.TotalMilliseconds;
                if (secondsSinceEpoch - lastseen > 500)
                {
                    listMed5.RemoveAt(loopr);
                    loopr = -1;
                }

            }

             Med1num = listMed1.Count();
             Med2num = listMed2.Count();
             Med3num = listMed3.Count();
             Med4num = listMed4.Count();
             Med5num = listMed5.Count();

            SetText(Med1num, Med2num, Med3num, Med4num, Med5num);

        } 
    }

像这样:

/// <summary>The group</summary>
enum eGroup : byte
{
    A, B, C, D, E
};
static IEnumerable<eGroup> allGroups()
{
    return Enum.GetValues( typeof( eGroup ) ).Cast<eGroup>();
}
/// <summary>Classify tag to the group, returns null if failed.</summary>
static eGroup? classify( Tag tag )
{
    string str = tag.Epc.ToString().ToUpper();
    foreach( eGroup e in allGroups() )
        if( str.Contains( e.ToString ) )
            return e;
    return null;
}

/// <summary>Add tag to list unless it's already there.</summary>
static void addToList( List<Tag> list, Tag t )
{
    int ind = list.IndexOf( t );
    if( ind < 0 )
        list.Add( t );  // Not on the list
    else if( list[ ind ].LastSeenTime.Utc < t.LastSeenTime.Utc )
        list[ ind ] = t; // Only replace if newer then the existing one
}

/// <summary>The collection to hold all that groups.</summary>
readonly Dictionary<eGroup, List<Tag>> med = allGroups().ToDictionary( e => e, e => new List<Tag>() );

/// <summary>True if that tag is too old.</summary>
static bool isTooOld( Tag t )
{
    ImpinjTimestamp ts = t.LastSeenTime;
    DateTime utc = new DateTime( 1970, 1, 1, 0, 0, 0, DateTimeKind.Utc ).AddTicks( (long)ts.Utc * 10L );
    return ( DateTime.UtcNow - utc ).TotalMilliseconds > 500;
}

void OnTagsReported( ImpinjReader sender, TagReport report )
{
    // Classify and add to the correct group
    foreach( Tag tag in report )
    {
        eGroup? group = classify( tag );
        if( !group.HasValue ) continue;
        addToList( med[ group.Value ], tag );
    }
    // Remove too old tags
    foreach( var list in med.Values )
        list.RemoveAll( isTooOld );

    // Show counts
    int[] c = allGroups().Select( e => med[ e ].Count ).ToArray();
    SetText( c[ 0 ], c[ 1 ], c[ 2 ], c[ 3 ], c[ 4 ] );
}

在我看来,这就是您可能需要的全部内容:

void OnTagsReported(ImpinjReader sender, TagReport report)
{
    tags.RemoveAll(tag => report.Select(r => r.Epc).Contains(tag.Epc));

    tags.AddRange(report);

    tags.RemoveAll(tag =>
    {
        Impinj.OctaneSdk.ImpinjTimestamp second = tag.LastSeenTime;
        string milisecondsUTC = second.ToString();
        long lastseen = Convert.ToInt64(milisecondsUTC);
        TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
        long secondsSinceEpoch = (long)t.TotalMilliseconds;
        return secondsSinceEpoch - lastseen > 5000;
    });

    var counts =
            new[] { "A", "B", "C", "D", "E", }
                .Select(c =>
                    tags
                        .Where(tag => tag.Epc.ToString().IndexOf(c) != -1)
                        .Count())
                .ToArray();

    SetText(counts[0], counts[1], counts[2], counts[3], counts[4]);
}

counts 定义可能是唯一令人悲伤的事情,但如果这种基本的方法无法为您提供所需的结果,我可以让它发挥作用。

确保代码顶部有 using System.Linq;


这是我为测试目的创建的对象模型。它允许 OP 的代码和我的代码编译。

public void SetText(params int[] xs) { }

public class Impinj
{
    public class OctaneSdk
    {
        public class TagData { }
        public class ImpinjTimestamp { }
    }
}

public class ImpinjReader { }

public class TagReport : List<Tag> { }

public class Tag
{
    public ushort AntennaPortNumber;
    public Impinj.OctaneSdk.TagData Epc;
    public Impinj.OctaneSdk.ImpinjTimestamp LastSeenTime;
}

如果你想防止重复,你应该考虑使用 Sets。根据您的需要,TreeSet 可用于排序数据,在这种情况下,您必须确保 Tag 实现 IComparable。否则 HashSet 应该很合适。
如果您想继续使用列表,您 必须 覆盖 Tag 上的 Equals 方法。根据您的代码,我们看不到 Tag 的结构,但是您使用了 List.Contains 方法。您应该实施 Equals 而忽略 LastTimeSeen。
您也不应该以这种方式使用 IndexOf,因为它有点模糊代码,您应该能够将其替换为 String.Contains。按照 Enigmativity 的建议使用 Linq 也可以大大清理您的代码。

如果您无权访问该库的源代码,您应该在检查相等性的选择集合上创建一个辅助方法或扩展方法。