根据列表的值使用 Linq 清除列表中的某些属性值

Clean some properties value in List with Linq based on a value of the list

我有一个对象“MyObject”,其属性(全是字符串):“PropA”、“PropB”和“PropC”。

var List = new List();

我在此列表中添加了一些具有以下值的对象:

List.Add(new MyObject { PropA = "AA", PropB = "1", PropC = "TT"});
List.Add(new MyObject { PropA = "AA", PropB = "1", PropC = "TT"});
List.Add(new MyObject { PropA = "AA", PropB = "1", PropC = "TT"});

List.Add(new MyObject { PropA = "BB", PropB = "1", PropC = "TT"});
List.Add(new MyObject { PropA = "BB", PropB = "1", PropC = "TT"});

使用 linq,我希望每个不同的“PropA”保留第一个记录,但设置为 string.Empty 另一个。我想要的结果是一个包含这些值的列表:

MyObject { PropA = "AA", PropB = "1", PropC = "TT"}
MyObject { PropA = "", PropB = "", PropC = "TT"}
MyObject { PropA = "", PropB = "", PropC = "TT"}
MyObject { PropA = "BB", PropB = "1", PropC = "TT"}
MyObject { PropA = "", PropB = "", PropC = "TT"}

我用 foreach 做了,但它在 Linq 中可能更干净一些,但必须保持结果的顺序。

这适用于特定情况:

var list = 
 List.GroupBy(x => x.PropA)
        .SelectMany(grp => new MyObject[] { grp.First() }
        .Concat(grp.Skip(1)
          .Select(x => { x.PropA = String.Empty; x.PropB = String.Empty; return x; } )
           )
         );

LinqPad 结果:

附带说明一下,我认为在这种情况下使用 Linq 是不合理的,它不会使代码更快或更清晰。必须使用可用的工具来编写更好、更高性能或更简洁的代码,但在这种情况下,我认为这并不比 foreach(至少是经过深思熟虑的 foreach,并且不是蛮力)以任何可能的方式。

这个?

string currentValue = "";
            List.OrderBy(x => x.PropA).ToList()ForEach(x =>
            {
                if (string.IsNullOrEmpty(currentValue))
                {
                    // Assuming PropA will never be null
                    currentValue = x.PropA;
                    // this is first element
                    return;
                }
                if (x.PropA == currentValue)
                {
                    x.PropA = "";
                }
                else
                {
                    currentValue = x.PropA;
                }

            });

这个怎么样:

    var result = List.GroupBy(prop => prop.PropA)
        .SelectMany(group => new [] { group.First() }.Concat(group.Skip(1).Select(x => { x.PropA = x.PropB = ""; return x; }))).ToList();

没有 LINQ

HashSet<string> propA = new HashSet<string>();
HashSet<string> propB = new HashSet<string>();

for (int i = 0; i < list.Count; i++)
{
    if (!propA.Add(list[i].PropA))
    {
        list[i].PropA = string.Empty;
    }

    if (!propB.Add(list[i].PropB))
    {
        list[i].PropB = string.Empty;
    }
}
var groups = List.GroupBy(obj => obj.PropA);
foreach (var group in groups)
{
    foreach (var item in group.Skip(1))
    {
        item.PropA = "";
        item.PropB = "";
    }
}

这个有趣问题的另一种解决方案:

var result =
    list
    .GroupBy(x => x.PropA, (key, items) => new { Key = key, Items = items })
    .Select(x => x.Items.Select((item, index) =>
    {
        if (index == 0) return item;
        item.PropA = string.Empty;
        item.PropB = string.Empty;
        return item;
    }))
    .SelectMany(x => x)
    .ToList();

它修改每个组中的原始对象,但第一个。这次没有 Concat.


有时一切都与性能有关。如果担心这个问题的人偶然发现了这个问题,答案是在这种情况下,一个简单的循环比建议的 linq 查询快大约四倍:

对于 1.000.000 项 linq 需要 ~200ms 和以下循环 ~45ms:

string prev = list.First().PropA;
foreach (var item in list.Skip(1))
{
    if (item.PropA == prev)
    {
        item.PropA = string.Empty;
        item.PropB = string.Empty;
    }
    else
    {
        prev = item.PropA;
    }
}