Linq:列表内的列表组

时间:2013-12-04 10:03:32

标签: linq group-by filtering

假设我有一份清单清单。 对于此列表中的每个项目,我都有一个自定义对象列表。

这些对象是这样的:

public string Field1
public string Field2
public string Field3

我想通过Linq实现:从我的列表列表中过滤掉所有具有相同三个字段的对象,这些字段不是列表中的第一个元素,只保留第一个元素。

因此,假设我的列表中有两个列表listA和列表B.

listA有三个对象object1,object2和object3。

object1.Field1 = "a"    object1.Field2 = "A"    object1.Field3 = "1"  
object2.Field1 = "a"    object2.Field2 = "B"    object2.Field3 = "2"  
object3.Field1 = "a"    object3.Field2 = "C"    object3.Field3 = "3"  

listB有三个对象object4,object5和object6。

object4.Field1 = "a"    object4.Field2 = "A"    object4.Field3 = "1"  
object5.Field1 = "a"    object5.Field2 = "B"    object5.Field3 = "2"  
object6.Field1 = "a"    object6.Field2 = "D"    object6.Field3 = "3" 

在此示例中,object1和object4是相同的,但由于它们是各自列表中的第一个,因此它们不会被过滤掉。 但是,object2和object5具有相同的三个字段值,只保留其中一个,以便在我的过程结束时,我将有两个列表如下:

listA有三个对象object1,object2和object3。

object1.Field1 = "a"    object1.Field2 = "A"    object1.Field3 = "1"  
object2.Field1 = "a"    object2.Field2 = "B"    object2.Field3 = "2"  
object3.Field1 = "a"    object3.Field2 = "C"    object3.Field3 = "3"

listB现在有两个对象object4和object6。

object4.Field1 = "a"    object4.Field2 = "A"    object4.Field3 = "1"  
object6.Field1 = "a"    object6.Field2 = "D"    object6.Field3 = "3" 

我几个小时都在摸不着头脑,但无济于事。我不能做一个foreach列表列表查看所有其他列表,因为它会导致性能问题(我可能有1000000个列表列表)。

有想法的人吗?

1 个答案:

答案 0 :(得分:0)

为什么它必须是LINQ?一个简单的迭代器块很好地解决了这个问题。

以下代码假设您已在对象中覆盖EqualsGetHashCode,以检查3个字段是否相等。如果无法做到这一点,请改用自定义相等比较器(在HashSet构造函数中传递)

static IEnumerable<List<T>> GetFilteredList<T>(IEnumerable<List<T>> input)
{
    var found = new HashSet<T>();

    foreach (var list in input)
    {
        var returnList = new List<T>(list.Capacity);
        foreach (var item in list)
        {
            // the first item is always added
            // any other items are only added if they were 
            // never encountered before
            if (list.Count == 0 || !found.Contains(item))
            {
                found.Add(item);
                returnList.Add(item);
            }
        }
        yield return returnList;
    }
}

如果您坚持使用IEnumerable<IEnumerable<T>>作为返回值,另一种只扫描输入一次的方法可能是这样的(不创建中间列表):

static IEnumerable<IEnumerable<T>> GetFilteredList<T>(IEnumerable<List<T>> input)
{
    var encounteredElements = new HashSet<T>();

    foreach (var list in input)
    {
        yield return Process(list, encounteredElements);
    }
}

static IEnumerable<T> Process<T>(IEnumerable<T> input, 
                                 HashSet<T> encounteredElements)
{
    bool first = true;
    foreach (var item in input)
    {
        if (first) yield return item;
        if (!encounteredElements.Contains(item))
        {
            yield return item;
        }
        encounteredElements.Add(item);
        first = false;
    }
}