【问题标题】:Remove Duplicate item from datatable that starts with alphabet从以字母开头的数据表中删除重复项
【发布时间】:2019-04-26 02:47:30
【问题描述】:

我正在尝试从数据表中删除重复数据,但不仅仅是保留第一个数据条目并继续删除第二个重复条目。我需要设置一个条件,以便它能够删除不正确的条目。

例如:

ID          Value
111          A
222          B
333          C
444          A

我想删除 111 数据并保留 444,因为它们有重复的数据 A。我找到的另一个解决方案将删除 444。 我能找到的与我的问题最接近的事情就是这个。 Remove Duplicate item from list based on condition

答案是使用我不熟悉的 linq。我正在考虑使用“StartsWith”来过滤我想要的正确数据,但我不知道如何实现它。

var result = items
    .GroupBy(item => item.Name)
    .SelectMany(g => g.Count() > 1 ? g.Where(x => x.Price != 500) : g); <-- I want to apply StartsWith here 

如果有人能帮我解决这个问题,我真的很感激。

【问题讨论】:

  • 为什么要删除 ID = 111 insetad 的 ID = 444 的条目??
  • 那么删除 111 并保留 444 的标准是什么?如果A => 111222333444555 有这样的 ID 怎么办?那么哪一个会被删除?

标签: c# linq


【解决方案1】:

我认为你需要类似的东西

var result = items
    .GroupBy(item => item.Name)
    .SelectMany(g =>
    {
       if (g.Count() > 1 && g.Key == "A") //g.Key.StartsWith("A")
         return g;
    });

这将返回一个数组,其中包含所有 "A" 元素,然后你可以决定要删除哪个

删除所有重复项并仅保留最后插入的元素:

var result = items
    .GroupBy(item => item.Name)
    .SelectMany(g =>
    {
       if (g.Count() > 1)
       {
          var mainElement = g.OrderByDescending(x => x.ID).First();
          return g.Where(x => x.ID != mainElement.ID).ToArray();
       }
    });

【讨论】:

    【解决方案2】:

    你忘了说为什么要保留第 444 项而不是第 111 项,而不是相反。

    LINQ 是为查询数据而开发的。 LINQ 永远不会更改原始源序列。

    您可以使用 LINQ 查询您要移除的项目,然后使用 foreach 将项目一一移除。

    查询具有重复项的项目很容易。如果您更频繁地需要此功能,请考虑为此创建一个扩展功能:

    static IEnumerable<IGrouping<TSource, TKey>> GetDuplicates<TSource>(
       this IEnumerable<TSource> source,
       Func<TSource, TKey> propertySelector)
    {
        // TODO: check source and propertySelector not null
    
        // make groups of source items that have the same value for property:
        return source.GroupBy(item => propertySelector(item))
    
            // keep only the groups that have more than one element
            // it would be a waste to Coun(), just stop after counting more than one
            .Where(group => group.Skip(1).Any());
    }
    

    这将为您提供具有所选属性重复值的所有源项的组。

    在你的情况下:

    var itemsWithDuplicateValues = mySourceItems.GetDuplicates(item => item.Value);
    

    这将为您提供所有具有 item.Value 重复值的源项目,按相同的 item.Value 分组

    现在您有时间了解为什么要保留 ID 为 444 而不是 111 的项目,您可以编写一个函数,该函数接受一组重复项并返回您要删除的元素。

    static IEnumerable<TSource> SelectItemsIWantToRemove<TSource>(
       IEnumerable<TSource> source)
    {
         // TODO: check source not null
         // select the items that you want to remove:
         foreach (var item in source)
         {
             if (I want to remove this item)
               yield return item;
         }
         // TODO: make sure there is always one item that you want to keep
         // or decide what to do if there isn't any item that you want to keep
    }
    

    现在您已经有了一个选择要删除的项目的函数,很容易创建一个 LINQ,该 LINQ 将从您的重复序列中选择要删除的项目:

    static IEnumerable<TSource> WhereIWantToRemove<TSource>(
       this IEnumerable<IGrouping<TSource>> duplicateGroups)
    {
        foreach (var group in duplicateGroups)
        {
            foreach (var sourceItem in group.WhereIWantToRemove())
            {
                yield return sourceItem;
            }
        }
    }
    

    您也可以为此使用SelectMany

    现在把所有东西放在一起:

    static IEnumerable<TSource> WhereIWantToRemove<TSource, TKey>(
       this IEnumerable<TSource> source,
       Func<TSource, TKey> propertySelector)
    {
        return source.GetDuplicates(propertySelector)
            .WhereIWantToRemove();
    }
    

    用法:

    var itemsToRemove = mySourceItems.WhereIWantToRemove(item => item.Value);
    

    您可以看到我选择创建几个相当小且易于理解的扩展函数。当然,您可以将它们全部放在一个大的 LINQ 语句中。但是,我不确定您是否可以说服您的项目负责人,这将使您的代码具有更好的可读性、可测试性、可维护性和可重用性。所以我的建议是坚持使用小的扩展功能。

    【讨论】:

      【解决方案3】:

      您可以将DataRows 按值分组,然后选择所有与您的条件不匹配的行,然后删除所有这些行:

      var result = items.AsEnumerable()
                        .GroupBy(item => item.Field<string>("Value"))
                        .Where(g => g.Count() > 1)
                        .SelectMany(g => g.Where(x => !x.Field<string>("ID").StartsWith("4")));
      foreach (var r in result) {
          r.Delete();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-28
        • 1970-01-01
        • 2019-05-18
        • 2019-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-07
        相关资源
        最近更新 更多