检查列表中的项目是否为增量项而非冗余项

时间:2018-05-04 07:12:05

标签: c# list linq

我需要保留所有冗余而非增量的列表。但到目前为止,我的代码仅适用于冗余的项目

_lst.Add(new MSheetValue
                    {
                        Column = 1,
                        Line = "1",
                        Pdf = "PDF1"
                    });

                    _lst.Add(new MSheetValue
                    {
                        Column = 1,
                        Line = "1",
                        Pdf = "PDF1"
                    });

                    _lst.Add(new MSheetValue
                    {
                        Column = 1,
                        Line = "1",
                        Pdf = "PDF2"
                    });


                    _lst.Add(new MSheetValue
                    {
                        Column = 1,
                        Line = "2",
                        Pdf = "PDF2"
                    });

                    _lst.Add(new MSheetValue
                    {
                        Column = 1,
                        Line = "3",
                        Pdf = "PDF2"
                    });


                    _lst.Add(new MSheetValue
                    {
                        Column = 1,
                        Line = "1",
                        Pdf = "PDF3"
                    });

                    _lst.Add(new MSheetValue
                    {
                        Column = 1,
                        Line = "3",
                        Pdf = "PDF3"
                    });

这是我的代码

var result = _lst.GroupBy(x => new { x.Line, x.Pdf })
               .Where(x => x.Skip(1).Any()).ToList();

,结果是

Column = 1,
Line = "1",
Pdf = "PDF1"

但我还需要非增量列表

所以我也需要这个

Column = 1,
Line = "1",
Pdf = "PDF3"


Column = 1,
Line = "3",
Pdf = "PDF3"

我该如何解决呢?我试图寻找解决方案并测试我发现的但我无法解决的问题。它没有回复我的预期

3 个答案:

答案 0 :(得分:0)

var distinctItems = _lst.Distinct();

要仅匹配某些属性,请创建自定义相等比较器,例如:

class DistinctItemComparer : IEqualityComparer<Item> {

public bool Equals(Item x, Item y) {
    return x.Column == y.Column &&
        x.Line == y.Line &&
        x.Pdf == y.Pdf;
}

public int GetHashCode(Item obj) {
    return obj.Column.GetHashCode() ^
        obj.Line.GetHashCode() ^        
        obj.Pdf.GetHashCode();
}

}

然后像这样使用它:

var distinctItems = _lst.Distinct(new DistinctItemComparer());

或尝试一下:

var distinctItems = _lst.GroupBy(x => x.Id).Select(y => y.First());

答案 1 :(得分:0)

使用zip来获取相邻的项目然后比较相邻的项目并选择不相邻的项目可能会有所帮助。这个例子有点过于简单,因为你可能想要将字段与Pdfs进行比较。联盟将重复项添加到非邻接区。

return _lst.Zip(_lst.Skip(1), (a, b) => new { a, b})
.Where(w => w.b.Line != w.a.Line + 1)
.Select(w => w.b)

.Union(_lst.GroupBy(x => new { x.Line, x.Pdf })
           .Where(x => x.Skip(1).Any()).ToList()
           .SelectMany(s => s));

答案 2 :(得分:0)

使用一些方便的扩展方法:

public static class Ext {
    public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combine) {
        using (var srce = src.GetEnumerator()) {
            if (srce.MoveNext()) {
                var prevkv = (seedKey, srce.Current);

                while (srce.MoveNext()) {
                    yield return prevkv;
                    prevkv = (combine(prevkv, srce.Current), srce.Current);
                }
                yield return prevkv;
            }
        }
    }
    public static IEnumerable<IGrouping<int, TRes>> GroupByWhile<T, TRes>(this IEnumerable<T> src, Func<T, T, bool> test, Func<T, TRes> result) =>
        src.ScanPair(1, (kvp, cur) => test(kvp.Value, cur) ? kvp.Key : kvp.Key + 1).GroupBy(kvp => kvp.Key, kvp => result(kvp.Value));

    public static IEnumerable<IGrouping<int, TRes>> GroupBySequential<T, TRes>(this IEnumerable<T> src, Func<T, int> SeqNum, Func<T, TRes> result) =>
        src.GroupByWhile((prev, cur) => SeqNum(prev) + 1 == SeqNum(cur), result);
    public static IEnumerable<IGrouping<int, T>> GroupBySequential<T>(this IEnumerable<T> src, Func<T, int> SeqNum) => src.GroupBySequential(SeqNum, e => e);

    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keyFun, IEqualityComparer<TKey> comparer = null) {
        var seenKeys = new HashSet<TKey>(comparer);
        foreach (var e in src)
            if (seenKeys.Add(keyFun(e)))
                yield return e;
    }

    public static int ToInteger(this string s) => Convert.ToInt32(s);
}

ScanPair是我的APL灵感Scan运算符的变体(类似Aggregate只返回中间结果)。我发现我使用元组进行了大量Scan来传递原始信息,因此ScanPair将中间结果与原始值结合起来。

使用ScanPairGroupByWhile在测试为真时对每个元素和组运行测试。 当每个元素序列号是连续的时,使用GroupByWhileGroupBySequential组。

DistinctBy根据键选择函数返回不同的对象。我欺骗并使用此而不是为IEqualityComparer创建MSheetValue

最后,ToInteger只是阅读流程的便利扩展。

使用这些扩展方法,处理_lst相对简单:

var nonSeq = _lst.GroupBy(m => m.Pdf) // need to test each Pdf
                 .Select(mg => mg.GroupBySequential(m => m.Line.ToInteger())) // get the sequential groups
                 .Where(mg => mg.Count() > 1) // keep the ones with non-sequential lines
                 // parse each non-sequential group into just the unique entries and flatten
                 .Select(mg => mg.SelectMany(m => m).DistinctBy(m => new { m.Column, m.Line, m.Pdf }));