根据列表的值使用 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;
}
}
我有一个对象“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;
}
}