从类型类列表中删除重复项

本文关键字:删除 类型 列表 | 更新日期: 2023-09-27 18:26:04

我有一个具有以下属性的类

id (类型: 唯一long (, name (类型: string (, 主要版本 ( VM ( (类型: long (, 次要版本 ( Vm ( (类型: long (

我创建了一个此类的列表,列表如下所示

ID    Name     VM    Vm
1     ssim     2     1
2     SSim     3     1
3     Counter  5     1
4     Counter  6     2
5     Counter  6     5

我想根据版本主要和次要版本从列表中删除重复项。最终列表应如下所示

ID    Name     VM    Vm
2     SSim     3     1
5     Counter  6     5

从类型类列表中删除重复项

像这样的东西,我认为:

public class Product
{
    public Product(long id, string name, int major, int minor)
    {
        this.Id = id;
        this.Name = name;
        this.Major = major;
        this.Minor = minor;
    }
    public long Id { get; set; }
    public int Major { get; set; }
    public int Minor { get; set; }
    public string Name { get; set; }
}
private static void Main()
{
    IEnumerable<Product> products = new List<Product>
                                    {
                                        new Product(1, "ssim", 2, 1),
                                        new Product(2, "SSim", 3, 1),
                                        new Product(3, "Counter", 5, 1),
                                        new Product(4, "Counter", 6, 2),
                                        new Product(5, "Counter", 6, 5)
                                    };
    IEnumerable<Product> distinctProducts =
        (from x in products group x by x.Name.ToLower() into g select g.OrderByDescending(y => y.Major).ThenByDescending(y => y.Minor).First()).OrderBy(x => x.Name).ToList();
}

因此,您需要每个名称的最大版本。

你可以像这样使用 linq 来做到这一点:

void Main()
{
    var versions = new List<Version>
    {
     new Version(1,2, "a"),
     new Version(1,3, "a"),
     new Version(1,3, "b"),
     new Version(1,4, "b"),
     new Version(1,1, "b"),
     new Version(2,3, "c")
    };
    var distinctVersions = versions
    .GroupBy(g => g.name.ToLowerInvariant())
    .Select(g => g.ToList().OrderBy(x => x.major).ThenBy(x => x.minor).Last())
    .ToList();
}

假设你的类是ProgramEntry

public class ProgramEntry {
    public long Id;
    public string Name;
    public long VM;
    public long Vm;
    public ProgramEntry (long id, string name, long vM, long vm) {
        Id = id;
        Name = name;
        VM = vM;
        Vm = vm;
    }
    public override string ToString () {
        return this.Id+":"+this.Name+"("+this.VM+"."+this.Vm+")";
    }
}

(是的,使用公共字段不是好的做法,但它只是一个快速而肮脏的解决方案(

现在,您可以按版本(首先是主要版本,然后是次要版本(对它们进行排序:

List<ProgramEntry> programs = new List<ProgramEntry>();
//fill list with programs
var order = programs.OrderBy(x => -x.VM).ThenBy(x => -x.Vm);

这导致IEnumerable<ProgramEntry>以最大的大调优先排序,如果是等效的大调,则以最大的小调优先排序。

接下来,您可以使用此重复过滤器来过滤出具有相同Name的元素:

List<ProgramEntry> result = order.DistinctBy(x => x.Name).ToList();

顺便说一下,DistinctByMoreLINQ 库的一部分。或者您可以使用扩展类自己实现它:

public static class Foo {
    public static IEnumerable<TSource> DistinctBy<TSource, TKey>
        (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
        HashSet<TKey> seenKeys = new HashSet<TKey>();
        foreach (TSource element in source) {
            if (seenKeys.Add(keySelector(element))) {
                yield return element;
            }
        }
    }
}

演示(使用 csharp 交互式 shell(:

$ csharp
Mono C# Shell, type "help;" for help
Enter statements below.
csharp> public class ProgramEntry {
      >  
      >     public long Id;
      >     public string Name;
      >     public long VM;
      >     public long Vm;
      >  
      >     public ProgramEntry (long id, string name, long vM, long vm) {
      >         Id = id;
      >         Name = name;
      >         VM = vM;
      >         Vm = vm;
      >     }
      >  
      >     public override string ToString () {
      >         return this.Id+":"+this.Name+"("+this.VM+"."+this.Vm+")";
      >     }
      >  
      > }
csharp> List<ProgramEntry> programs = new List<ProgramEntry>();
csharp> programs.Add(new ProgramEntry(1,"ssim",2,1));
csharp> programs.Add(new ProgramEntry(2,"ssim",3,1)); 
csharp> programs.Add(new ProgramEntry(3,"Counter",5,1));
csharp> programs.Add(new ProgramEntry(4,"Counter",6,2)); 
csharp> programs.Add(new ProgramEntry(5,"Counter",6,5)); 
csharp> programs
{ 1:ssim(2.1), 2:ssim(3.1), 3:Counter(5.1), 4:Counter(6.2), 5:Counter(6.5) }
csharp> var order = programs.OrderBy(x => -x.VM).ThenBy(x => -x.Vm);
csharp> order
{ 5:Counter(6.5), 4:Counter(6.2), 3:Counter(5.1), 2:ssim(3.1), 1:ssim(2.1) }
csharp> List<ProgramEntry> result = order.DistinctBy(x => x.Name).ToList();
csharp> result
{ 5:Counter(6.5), 2:ssim(3.1) }

这是预期的行为吗?

假设这个类类似于你的数据:

public class VerX
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int VerMajor { get; set; }
    public int VerMinor { get; set; }
}

对于您的示例,这是填充此数据的方式:

var list = new List<VerX>
{
    new VerX { ID = 1, Name = "ssim", VerMajor = 2, VerMinor = 1 },
    new VerX { ID = 2, Name = "SSim", VerMajor = 3, VerMinor = 1 },
    new VerX { ID = 3, Name = "Counter", VerMajor = 5, VerMinor = 1 },
    new VerX { ID = 4, Name = "Counter", VerMajor = 6, VerMinor = 2 },
    new VerX { ID = 5, Name = "Counter", VerMajor = 6, VerMinor = 5 },
};

现在,让我们创建一个循环,为您提供所需的结果:

// First create new list that would hold the results
var listNew = new List<VerX>();
// Select distinct names from data (using ToLower, so casing does not matter)
var names = list.Select(t => t.Name.ToLower()).Distinct().ToList();
// Loop through each of distinct name
foreach (var name in names)
{
    // With LINQ, select item whose name matches and sort list by VerMajor
    // descending and VerMinor descending and take first item.
    var item = list.Where(t => t.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
                   .OrderByDescending(t => t.VerMajor)
                   .ThenByDescending(t => t.VerMinor)
                   .FirstOrDefault();
    // If item not found (although it should be found!), continue the loop
    if (item == null)
        continue;
    // Add item to new list
    listNew.Add(item);
}
// At the end of the loop, the listNew contains items as in your proposed result.

可以通过更复杂的 LINQ 查询获得相同的 foreach 循环:

// Select distinct names as in first case
var names = list.Select(t => t.Name.ToLower()).Distinct().ToList();
// Construct listNew from names based on same algorithm as before, but using LINQ this time.
var listNew = names
    .Select(name => list.Where(t => t.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
                        .OrderByDescending(t => t.VerMajor)
                        .ThenByDescending(t => t.VerMinor)
                        .FirstOrDefault())
    .Where(item => item != null)
    .ToList();
 // Here, listNew contains your desired result.

根据您想要的结果,这将为您提供按名称分组的结果,基于最大VerMajor和最大VerMinor。

我认为您的问题可以通过以下代码轻松完成:

var groupsByName = myItems.GroupBy(x => x.Name.ToLower());
var distinctItems = groupsByName.Select(x => x.ToList()
                                .OrderByDescending(y => y.VM)
                                .ThenByDescending(z => z.Vm).First())
                                .OrderBy(k => k.Name).ToList();