【问题标题】:How to merge multiple consecutive values in a list?如何合并列表中的多个连续值?
【发布时间】:2019-08-27 04:09:40
【问题描述】:

是否有删除结果值的功能(即14, 14 -> 1412, 12 -> 12)?

以下列表([12, 14, 14, 12, 12, 14]):

List<string> foo = new List<string> { 12, 14, 14, 12, 12, 14 };

到列表[12, 14, 12, 14]?

【问题讨论】:

  • 之前肯定有人问过这个问题。
  • @Micha 可能你是对的,但用我自己的话找不到。
  • 您可以遍历列表,并跟踪最后看到的元素。如果最后看到的元素与当前元素匹配,则继续,否则将最后看到的元素更新为当前值并产生当前值以供进一步使用。

标签: c# list distinct


【解决方案1】:

Linq 没有额外的库,但有 副作用 是一个又快又脏prior 副作用是丑陋的)解决方案:

  List<string> foo = new List<string> { "12", "14", "14", "12", "12", "14" };

  string prior = null;

  List<string> result = foo
    .Where((v, i) => i == 0 || v != prior)
    .Select(v => prior = v)
    .ToList();

一般情况中,您可能想要实现一个扩展方法

  public static partial class EnumerableExtensions {  
    public static IEnumerable<T> DistinctSuccessive<T>(
      this IEnumerable<T> source, 
           IEqualityComparer<T> comparer = null) {
      // public method arguments validation
      if (null == source)
        throw new ArgumentNullException(nameof(source));

      // equals: either default or custom one 
      Func<T, T, bool> equals = (left, right) => null == comparer 
        ? object.Equals(left, right) 
        : comparer.Equals(left, right);

      bool first = true;
      T prior = default(T);

      foreach (var item in source) {
        if (first || !equals(item, prior))
          yield return item;

        first = false;
        prior = item;
      }
    }
  }

然后

  List<string> result = foo
    .DistinctSuccessive()
    .ToList();

【讨论】:

    【解决方案2】:

    我个人更喜欢@fubo 的答案,但只是为了表明还有更多变体:

    var data = new[] { 12, 14, 14, 12, 12, 14 };
    var result = data.Aggregate(new List<int>(), (a, e) => { if (a.FirstOrDefault() != e) a.Insert(0, e); return a; });
    result.Reverse(); // we builded a list in a back order, so reverse it!
    

    【讨论】:

    • 非常低效,因为LastOrDefault 调用。
    • @TheodorZoulias,真的。仅适用于短名单。我应该编辑我的答案。
    • @TheodorZoulias 这个怎么样? :)
    • @vasily.sib 非常出色。 :-)
    【解决方案3】:

    使用foreach接近

    public static IEnumerable<T> DistinctByPrevious<T>(List<T> source)
    {
        if (source != null && source.Any())
        {
            T prev = source.First();
            yield return prev;
            foreach (T item in source.Skip(1))
            {
                if (!EqualityComparer<T>.Default.Equals(item, prev))
                {
                    yield return item;
                }
                prev = item;
            }
        }
    }
    

    【讨论】:

    • 很好,但在一般情况中,如果IEnumerable&lt;T&gt; 不是一个物化集合,而是一个文件 - File.ReadLines("c:\myFile.txt") 你可以有source.Any()不一致结果 - 第一次实现; source.First() - 第二个具体化和foreach(...source.Skip(1)) - 第三个。
    • @DmitryBychenko 能否请您指出规范的相关部分(如果三个是这样的话)。我想更详细地了解为什么会出现问题,最好是基于规范。
    • @Micha Wiedenmann:想象source 是一个文件,例如File.ReadLines("c:\MyFile.csv")。然后source.Any() 将打开文件并尝试读取第一行,如果第一行存在,它将返回true。接下来我们调用source.First(),但文件可能已更改(例如清除),现在我们在空序列上抛出异常First()
    • @DmitryBychenko 我已将答案更改为List&lt;T&gt; 以避免这种特殊情况 - SQLDataReader 也有同样的问题
    • 基于这种严格程度,即使 List 也不好,因为它也可以并行更改。事实上,除了数组和不可变类型之外,我看不到在满足此约束的同时使用 LINQ 的任何机会。 – Micha Wiedenmann 35 分钟前
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-04
    • 2019-08-12
    • 2013-01-22
    • 2020-06-08
    • 1970-01-01
    • 2017-05-17
    • 2022-06-13
    相关资源
    最近更新 更多