获取列表中的最后一个重复元素

Posted

技术标签:

【中文标题】获取列表中的最后一个重复元素【英文标题】:Get last duplicate element in a list 【发布时间】:2022-01-01 20:28:34 【问题描述】:

我有一个包含重复项的列表。

List<string> filterList = new List<string>()

     "postpone", "access", "success", "postpone", "success"
;

我通过使用得到postpone, access, success 的输出

List<string> filter = filterList.Distinct().ToList();
string a = string.Join(",", filter.Select(a => a).ToArray());
Console.WriteLine(a);

我看过其他例子,他们可以使用groupby 来获取最新的元素,因为他们有其他项目,比如 ID 等。现在我只有字符串,我怎样才能得到列表中的最新项目 @987654325 @?有什么建议吗?

【问题讨论】:

嗯,string.Split(a, ',').Last().Trim()?我不太清楚背景是如何相关的,如果问题真的是关于你可以用最后的字符串做什么。 (您的标题谈到“”,但实际上是“获取由逗号分隔的字符串表示的列表的最后一个元素”) “最新”access 与第一个有何不同?它是相同的字符串,还是我在这里遗漏了什么? 【参考方案1】:

一种方法是使用原始集合中项目的索引以及 GroupBy。例如,

    var lastDistinct = filterList.Select((x,index)=> new Value=x,Index=index)
                                 .GroupBy(x=>x.Value)
                                 .Select(x=> x.Last())
                                 .OrderBy(x=>x.Index)
                                 .Select(x=>x.Value);
    var result = string.Join(",",lastDistinct);

输出

access,postpone,success

【讨论】:

欣赏这个很好的例子。帮助很大。【参考方案2】:

OrderedDictionary 执行此操作。您所要做的就是使用“如果它在字典中,请删除它。添加它”的逻辑将您的项目添加到其中。 OrderedDictionary 保留了添加顺序,因此通过删除较早添加的内容并重新添加它会跳转到字典的末尾

var d = new OrderedDictionary();
filterList.ForEach(x =>  if(d.Contains(x)) d.Remove(x); d[x] = null; );

您的d.Keys 现在是一个字符串列表

access
postpone
success

OrderedDictionary 位于 Collections.Specialized 命名空间中

如果您希望密钥为 CSV,您可以使用 Cast 将它们从对象转换为字符串

var s = string.Join(",", d.Keys.Cast<string>());

【讨论】:

感谢您的精彩解释.....我学到了新东西'OrderedDictionary'。【参考方案3】:

您的输入列表只是字符串类型,因此使用 groupBy 并不会真正添加任何内容。如果您考虑您的代码,您的第一行为您提供了不同的列表,您只会丢失不同的项目,因为您在第 2 行执行了 string.join。您需要做的就是在加入之前添加一行:

List<string> filter = filterList.Distinct().ToList();
string last = filter.LastOrDefault();
string a = string.Join(",", filter.Select(a => a).ToArray());
Console.WriteLine(a);

我想你可以让你的代码更简洁,因为你在调用 string.Join 时既不需要 .Select(a => a) 也不需要 .ToArray()。

如果您有一个类/结构/记录/元组项目列表,您可能希望按特定键(或多个键)进行分组,而不是在整个事物上使用 Distinct(),则将使用 GroupBy。 GroupBy 非常有用,您应该了解这一点,以及 ToDictionary 和 ToLookup LINQ 帮助器功能。

【讨论】:

【参考方案4】:

那么为什么不应该返回第一次出现的“推迟”呢?因为稍后在序列中您会再次看到同一个词“推迟”。为什么要返回第一次出现的“访问”?因为在后面的序列中你再也看不到这个词了。

所以:如果序列的其余部分没有这个词,则返回一个词。

这在 LINQ 中很容易,使用递归,但效率不高:对于每个单词,您必须检查序列的其余部分以查看该单词是否在其余部分。

记住找到单词的最高索引会更有效率。

作为一种扩展方法。如果您对扩展方法不熟悉,请参阅extension methods demystified。

private static IEnumerable<T> FindLastOccurences<T>(this IEnumerable<T> source)

    return FindLastOccurrences<T>(source, null);


private static IEnumerable<T> FindLastOccurences<T>(this IEnumerable<T> source,
    IEqualityComparer<T> comparer)

    // TODO: check source not null
    if (comparer == null) comparer = EqualityComparer<T>.Default;

    Dictionary<T, int> dictionary = new Dictionary<T, int>(comparer);

    int index = 0;
    foreach (T item in source)
    
        // did we already see this T? = is this in the dictionary
        if (dictionary.TryGetValue(item, out int highestIndex))
        
            // we already saw it at index highestIndex.
            dictionary[item] = index;
        
        else
        
            // it is not in the dictionary, we never saw this item.
            dictionary.Add(item, index);
        
        ++index;
    

    // return the keys after sorting by value (which contains the highest index)
    return dictionay.OrderBy(keyValuePair => keyValuePair.Value)
                    .Select(keyValuePair => keyValuePair.Key);

         

所以对于源序列中的每个项目,我们检查它是否在字典中。如果没有,我们将该项作为键添加到字典中。值就是索引。

如果它已经在字典中,则该值是我们之前找到该项目的最高索引。显然当前索引更高,所以我们替换字典中的值。

最后我们对字典中的键值对进行升序排序,只返回键。

【讨论】:

以上是关于获取列表中的最后一个重复元素的主要内容,如果未能解决你的问题,请参考以下文章

从 List<DTO> 获取第一个和最后一个元素 [重复]

获取数组中的第一个和最后一个元素,ES6方式[重复]

列表中的索引元素[重复]

如何将元素添加到R中的列表(循环)[重复]

获取列表的最后一个元素

从列表中获取总和为值的元素数组[重复]