【问题标题】: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 与第一个有何不同?它是相同的字符串,还是我在这里遗漏了什么?

标签: c# linq


【解决方案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);
    }
             
    

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-18
      • 1970-01-01
      • 2012-10-21
      • 2010-10-30
      • 1970-01-01
      • 2022-11-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多