2015-12-08 3 views
2

У меня есть класс с следующими свойствамиудалить дубликаты из списка класса типа

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 

Я хотел бы удалить дубликаты из списка на основе версии Major, а затем версии minor. Окончательный список должен выглядеть следующим образом

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

Если порядок вещей сохраниться? –

+0

Также вы используете список или хеш-таблицу? Это не одно и то же, поэтому вам не нужно использовать оба тега. –

+0

Из вашего примера окончательного списка похоже, что вы хотите сохранить имена на основе max VersionMajor и VersionMinor, а не удалять дубликаты. – Jure

ответ

1

Скажите ваш класс 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>, заказанный с наибольшим крупным первым, а в случае эквивалентного крупного, самого большого второстепенного.

Далее вы можете использовать this duplicate filter, чтобы отфильтровать элементы с тем же 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; 
      } 
     } 
    } 

} 

Demo (с использованием csharp интерактивной оболочки):

$ 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) } 

Является ли это ожидаемое поведение?

+0

У меня нет опции distincyBy. – Abe

+0

@Abe: как указано в ответе, это часть библиотеки [* MoreLINQ *] (https://github.com/morelinq/MoreLINQ). Но вы можете реализовать его самостоятельно, используя метод расширения (см. Обновленный ответ). –

+0

спасибо простой вопрос. Как я называю этот метод Foo.DistinctBy() Я не уверен, что будут paramteres. Я не очень хорошо знаком с linq. – Abe

2

Что-то вроде этого, я думаю:

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(); 
} 
1

Так что вы хотите максимальную версию каждого имени.

Вы можете сделать это с помощью 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(); 
} 
+0

Отличная демонстрация LINQ;) Может быть, вы можете просто добавить '.ToLower()', когда группируете по имени, чтобы сделать регистр без учета регистра. – Jure

+0

Спасибо, и это хороший момент, я изменил его. – Carra

0

Давайте предположим, что этот класс напоминает ваши данные:

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:

// 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.

+0

Мы должны сначала отсортировать его по версии major и version minor. Затем удалите отдельные имена, иначе я могу потерять данные, если версия больше, но имя дублируется. Надеюсь, это имеет смысл. – Abe

+0

Не совсем понимаю. Кажется, что вы хотите сохранить имена (и другие данные, например ID) с самой высокой версией (VerMajor, VerMinor). Это означает, что сначала группируется по имени, а затем сохраняет только самую высокую версию. – Jure

0

Я думаю, что вы спрашиваете можно легко сделать с этим кодом:

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(); 
Смежные вопросы