从类型类列表中删除重复项
本文关键字:删除 类型 列表 | 更新日期: 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();
顺便说一下,DistinctBy
是 MoreLINQ 库的一部分。或者您可以使用扩展类自己实现它:
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();