2010-05-08 2 views
2

Это аналогичный к моему последнему вопросу; но под другим углом. See if item exists once in Enumerable (Linq)Сравнение равенств с несколькими экземплярами/IEqualityComparer проблемы в LINQ

Учитывая следующий набор элементов, а также списки, содержащие их ...

Item 1 
Item 2 
Item 3 
Item 4 
Item 5 

class Item 
{ 
string Name { get; set; } 
} 

List<Item> available = new List<Item>() 
{ 
Item 1 
Item 1 
Item 2 
Item 3 
Item 5 
} 

List<Item> selected = new List<Item>() 
{ 
Item 1 
Item 2 
Item 3 
} 

мне нужно сделать третий список, который имеет все, от «доступен», за исключением того, что в «выбран». Однако «Пункт 1» находится в «доступном» дважды, но только в «выбранном» один раз. Поскольку они являются экземплярами одного и того же элемента, у меня возникли проблемы с определением соответствующей логики для его размещения.

Окончательный массив должен выглядеть ...

List<Item> selectable = new List<Item>() 
{ 
Item 1 
Item5 
} 

ответ

1

Там может быть способ, LINQ для выполнения этой задачи. Вы могли бы получить 5 предметов, поскольку они уникальны, но второй пункт 1 может оказаться сложным. Однако вы всегда можете сделать это старомодным способом и создать новый список самостоятельно. Рассмотрим следующий пример:

class Item 
{ 
    public Item(string name) { Name = name; } 
    public string Name { get; set; } 
} 

...

List<Item> available = new List<Item>() 
{ 
    new Item("1"), new Item("1"), new Item("2"), new Item("3"), new Item("5") 
}; 

List<Item> selected = new List<Item>() 
{ 
    new Item("1"),new Item("2"), new Item("3") 
}; 

List<Item> stillAvailable = new List<Item>(); 
List<Item> stillSelected = new List<Item>(selected); 

foreach (Item item in available) 
{ 
    Item temp = stillSelected.Find(i => i.Name == item.Name); 
    if (temp == null) 
     stillAvailable.Add(item); 
    else 
     stillSelected.Remove(temp); 
} 

Вы можете создать список для элементов все еще доступны, который изначально пуст. Вы создаете список для выбранных элементов, который содержит все выбранные элементы. Затем вы просто перебираете доступные элементы и выполняете поиск в списке stillSelected. Если элемент найден, вы удалите его из списка stillSelected. Если нет, добавьте его в список stillAvailable. К концу цикла stillAvailable будет содержать один элемент 1 и пункт 5.

2

Это своего рода сложный подход, но он выполняет свою работу. Я позаимствовал из идиомы «Украсить-Сортировать-Undecorate» в Python, которая сортирует, связывая временный ключ сортировки с массивом, в сочетании с забавным и полезным фактом, что анонимные типы в .NET имеют стандартный EqualityComparer, который сравнивается на основе значения их полей.

Шаг 1: Группа элементов в каждом списке по имени, а затем связать индекс с каждым элементом внутри каждой группы и сглаживать группы обратно в обычный список:

var indexedAvailable = available.GroupBy(i => i.Name) 
           .SelectMany(g => g.Select((itm, idx) => new 
               { Name = itm.Name, Index = idx })); 
var indexedSelected = selected.GroupBy(i => i.Name) 
           .SelectMany(g => g.Select((itm, idx) => new 
               { Name = itm.Name, Index = idx })); 

Это превратит списки в них:

indexedAvailable   indexedSelected 
Name = Item 1, Index = 0 Name = Item 1, Index = 0 
Name = Item 1, Index = 1 Name = Item 2, Index = 0 
Name = Item 2, Index = 0 Name = Item 3, Index = 0 
Name = Item 3, Index = 0 
Name = Item 5, Index = 0 

Теперь вы можете видеть, что в индексируемых списках, каждый нумеруются появление любого имени в available будет соответствовать только с го e эквивалент номер с таким же именем в selected. Таким образом, вы можете использовать простой Except, чтобы удалить что-нибудь в indexedAvailable, что не в indexedSelected, а затем «undecorate» поворотом анонимных типизированных индексированные объекты обратно в Item с

var selectable = indexedAvailable.Except(indexedSelected) 
           .Select(i => new Item() { Name = i.Name }); 

И доказательство:.

foreach (var item in selectable) 
    Console.WriteLine(item.Name); 
//prints out: 
//Item 1 
//Item 5 

Обратите внимание, что это будет работать, даже если selected содержит имена, отсутствующие в available, напримересли во втором списке есть Item 4, как в вашем последнем вопросе.

1
var comp = new MyEqualityComparer(); 
selectable = available.Distinct(comp).Except(selected, comp); 
Смежные вопросы