LINQ中与多个实例/ IEqualityComparer问题的等式比较

时间:2010-05-08 02:11:48

标签: c# linq

这与我上一期的问题相似;但是从不同的角度来看。 See if item exists once in Enumerable (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
}

我需要创建一个包含“available”的第三个List,除了“selected”中的内容。 但是,“第1项”处于“可用”状态两次,但仅在“已选择”一次。由于它们是同一项目的实例,因此我无法找出适当的逻辑来适应这一点。

最终的数组应该看起来像......

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

3 个答案:

答案 0 :(得分:2)

这是一种有点棘手的方法,但它完成了工作。我借用了Python中的“Decorate-Sort-Undecorate”习语,它通过将临时排序键与数组相关联来进行排序,并结合.NET中的匿名类型具有默认EqualityComparer的有趣且有用的事实,该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中任何名称的每个编号出现次数仅与等效编号的出现的匹配项匹配selected中的同名。因此,您可以使用简单的Except删除不在indexedAvailable中的indexedSelected中的任何内容,然后通过将匿名类型的索引对象转回{{1}来“删除” }第

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

答案 1 :(得分: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 :(得分:1)

var comp = new MyEqualityComparer();
selectable = available.Distinct(comp).Except(selected, comp);