如何使用 LINQ 或 Lambda 代替嵌套和多个 foreach 语句
How to use LINQ or Lambda instead of nested and multiple foreach statements
如何使用 LINQ 或 Lambda 代替嵌套和多个 foreach 语句。
我想使用比嵌套 foreach 语句更好的语法来用第二个列表中的项目覆盖初始列表。
在下面的代码中:
- 我想用 secondList 中具有相同值的那些覆盖 initialList。 (去掉红色)
使用 secondList 中值相同的项目(黄色)
新的 initialList 列表应包括(绿色和黄色)
static void Main(string[] args)
{
int useProd = 2;
int useDomain = 0;
var person1 = new Person() { prodId = 1, Value = "foo", domainId = 0, Name = "Red" };
var person2 = new Person() { prodId = 1, Value = "bar", domainId = 0, Name = "Green" };
var person3 = new Person() { prodId = 1, Value = "foo", domainId = 1, Name = "Yellow" };
var initialList = new List<Person>();
initialList.Add(person1);
initialList.Add(person2);
var secondList = new List<Person>();
secondList.Add(person3);
List<Person> personsToRemove = new List<Person>();
List<Person> personsToUpdate = new List<Person>();
foreach (var pers1 in initialList)
{
foreach (var pers2 in secondList)
{
if (pers1.Value == pers2.Value)
{
personsToRemove.Add(pers1);
personsToUpdate.Add(pers2);
}
}
}
foreach (var remPers in personsToRemove)
{
initialList.Remove(remPers);
}
foreach (var updPers in personsToUpdate)
{
initialList.Add(updPers);
}
foreach (var item in initialList)
{
Console.WriteLine(String.Format("Value: {0}, prodId: {1}, domainId: {2}, Name: {3}", item.Value, item.prodId, item.domainId, item.Name));
}
Console.ReadKey();
}
public class Person
{
public int prodId { get; set; }
public string Value { get; set; }
public int domainId { get; set; }
public string Name { get; set; }
}
您的嵌套循环用 join
表示最有效。此外,在效率方面,不必对整个列表进行线性搜索只是为了删除一个元素然后添加一个新元素,这将有助于提高效率。我们可以使用 Enumerable.Select()
的重载在结果中嵌入项目索引,以便可以直接替换元素。
把它们放在一起,看起来像这样:
var join = from p1 in initialList.Select((p, i) => new { Person = p, Index = i })
join p2 in secondList on p1.Person.Value equals p2.Value
select new { Index = p1.Index, Replacement = p2 };
foreach (var item in join.ToList())
{
initialList[item.Index] = item.Replacement;
}
以上代码替换了以 personsToRemove
和 personsToUpdate
列表的声明开始的原始代码,以及前三个 foreach
循环(即除了显示最终结果)。
备注:
- 代码从
initialList
合成一个匿名类型,其中包含 Person
实例和该实例在列表中的索引。
join
子句将 Value
属性相等的每个列表中的所有项目配对。
重要提示: 如果任一列表中有多个元素具有相同的 Value
属性,则它们各自与另一个列表中的所有其他元素配对具有相同 Value
的列表。 IE。如果 initialList
有两个元素具有 "foo" 的 Value
,而 secondList
有三个这样的元素,那么您将得到 六个 元素在生成的连接中。您的问题没有定义这是否可能,也没有定义您希望发生什么,所以我在这里忽略了这种可能性。 :)
join
结果被投影到一个新的匿名类型,其中包含要替换的元素的索引和新值。
- 查询结果通过调用
ToList()
具体化。这是必要的,因为否则会延迟连接并且修改 initialList
会使查询无效。
- 当然,在剩下的
foreach
中,代码接下来需要做的就是将查询确定的替换值分配给列表中适当的索引位置。
您也可以使用泛型。以下是适合您的短代码:
initialList.ForEach(p =>
{
if (secondList.Any(sp => sp.Value == p.Value))
{
initialList.Remove(p);
initialList.Add(secondList.Single(spu => spu.Value == p.Value));
};
});
如何使用 LINQ 或 Lambda 代替嵌套和多个 foreach 语句。
我想使用比嵌套 foreach 语句更好的语法来用第二个列表中的项目覆盖初始列表。
在下面的代码中:
- 我想用 secondList 中具有相同值的那些覆盖 initialList。 (去掉红色)
使用 secondList 中值相同的项目(黄色) 新的 initialList 列表应包括(绿色和黄色)
static void Main(string[] args) { int useProd = 2; int useDomain = 0; var person1 = new Person() { prodId = 1, Value = "foo", domainId = 0, Name = "Red" }; var person2 = new Person() { prodId = 1, Value = "bar", domainId = 0, Name = "Green" }; var person3 = new Person() { prodId = 1, Value = "foo", domainId = 1, Name = "Yellow" }; var initialList = new List<Person>(); initialList.Add(person1); initialList.Add(person2); var secondList = new List<Person>(); secondList.Add(person3); List<Person> personsToRemove = new List<Person>(); List<Person> personsToUpdate = new List<Person>(); foreach (var pers1 in initialList) { foreach (var pers2 in secondList) { if (pers1.Value == pers2.Value) { personsToRemove.Add(pers1); personsToUpdate.Add(pers2); } } } foreach (var remPers in personsToRemove) { initialList.Remove(remPers); } foreach (var updPers in personsToUpdate) { initialList.Add(updPers); } foreach (var item in initialList) { Console.WriteLine(String.Format("Value: {0}, prodId: {1}, domainId: {2}, Name: {3}", item.Value, item.prodId, item.domainId, item.Name)); } Console.ReadKey(); } public class Person { public int prodId { get; set; } public string Value { get; set; } public int domainId { get; set; } public string Name { get; set; } }
您的嵌套循环用 join
表示最有效。此外,在效率方面,不必对整个列表进行线性搜索只是为了删除一个元素然后添加一个新元素,这将有助于提高效率。我们可以使用 Enumerable.Select()
的重载在结果中嵌入项目索引,以便可以直接替换元素。
把它们放在一起,看起来像这样:
var join = from p1 in initialList.Select((p, i) => new { Person = p, Index = i })
join p2 in secondList on p1.Person.Value equals p2.Value
select new { Index = p1.Index, Replacement = p2 };
foreach (var item in join.ToList())
{
initialList[item.Index] = item.Replacement;
}
以上代码替换了以 personsToRemove
和 personsToUpdate
列表的声明开始的原始代码,以及前三个 foreach
循环(即除了显示最终结果)。
备注:
- 代码从
initialList
合成一个匿名类型,其中包含Person
实例和该实例在列表中的索引。 join
子句将Value
属性相等的每个列表中的所有项目配对。
重要提示: 如果任一列表中有多个元素具有相同的 Value
属性,则它们各自与另一个列表中的所有其他元素配对具有相同 Value
的列表。 IE。如果 initialList
有两个元素具有 "foo" 的 Value
,而 secondList
有三个这样的元素,那么您将得到 六个 元素在生成的连接中。您的问题没有定义这是否可能,也没有定义您希望发生什么,所以我在这里忽略了这种可能性。 :)
join
结果被投影到一个新的匿名类型,其中包含要替换的元素的索引和新值。- 查询结果通过调用
ToList()
具体化。这是必要的,因为否则会延迟连接并且修改initialList
会使查询无效。 - 当然,在剩下的
foreach
中,代码接下来需要做的就是将查询确定的替换值分配给列表中适当的索引位置。
您也可以使用泛型。以下是适合您的短代码:
initialList.ForEach(p =>
{
if (secondList.Any(sp => sp.Value == p.Value))
{
initialList.Remove(p);
initialList.Add(secondList.Single(spu => spu.Value == p.Value));
};
});