我如何使用linq优化嵌套的for循环

时间:2012-12-29 19:51:12

标签: c# linq optimization linq-to-entities

我如何用linq写这个?

foreach (var to in allCurrentTradeObjects)
{
    foreach (var ro in theseWantMe)
    {
        if (ro.Type != to.Type
                || ro.MaxRent < to.Rent
                || ro.MinRooms > to.Rooms
                || ro.MinSquareMeters > to.SquareMeters
                || ro.MaxPrice < to.Price
                || ro.MinFloors > to.Floors
                || ro.TradeObjectId == to.TradeObjectId
                || ro.TradeObjectId == myTradeObject.TradeObjectId)
        {
            continue;
        }
        RatingListTriangleModel rlt = new RatingListTriangleModel
        {
            To1Id = myTradeObject.TradeObjectId,
            To2Id = to.TradeObjectId,
            To3Id = ro.TradeObjectId,
            T1OnT2Rating = 0,
            T2OnT3Rating = 0,
            T3OnT1Rating = 0,
            TotalRating = 0
        };

        //_context.RatingListTriangle.Add(rlt);
        this.InsertOrUpdate(rlt);

    }
}
this.Save();

5 个答案:

答案 0 :(得分:5)

var query = from to in allCurrentTradeObjects
            from ro in theseWantMe
            where ro.Type == to.Type &&
                  ro.MaxRent >= to.Rent &&
                  ro.MinRooms <= to.Rooms &&
                  ro.MinSquareMeters <= to.SquareMeters &&
                  ro.MaxPrice >= to.Price &&
                  ro.MinFloors <= to.Floors &&
                  ro.TradeObjectId != to.TradeObjectId &&
                  ro.TradeObjectId != myTradeObject.TradeObjectId
            select new RatingListTriangleModel
            {
                To1Id = myTradeObject.TradeObjectId,
                To2Id = to.TradeObjectId,
                To3Id = ro.TradeObjectId,
                T1OnT2Rating = 0,
                T2OnT3Rating = 0,
                T3OnT1Rating = 0,
                TotalRating = 0
            };

foreach(var rlt in query)
   this.InsertOrUpdate(rlt);

this.Save(); 

答案 1 :(得分:2)

首先将嵌套循环的骨架转换为LINQ:

 var rtls = allCurrentTradeObjects
     .SelectMany(to => theseWantMe.Select(ro => new {to, ro}));

这会为您提供一对{to, ro}对的列表。现在通过反转continue条件来添加过滤:

 var rtls = allCurrentTradeObjects
     .SelectMany(to => theseWantMe.Select(ro => new {to, ro}));
     .Where(p => p.ro.Type == p.to.Typpe && p.ro.MaxRent >= p.to.Rent && ...)

最后,添加Select来调用`new:

 var rtls = allCurrentTradeObjects
     .SelectMany(to => theseWantMe.Select(ro => new {to, ro}));
     .Where(p => p.ro.Type == p.to.Typpe && p.ro.MaxRent >= p.to.Rent && ...)
    .Select(p => new RatingListTriangleModel {
        To1Id = myTradeObject.TradeObjectId,
        To2Id = p.to.TradeObjectId,
        To3Id = p.ro.TradeObjectId,
        ...
    });

手持rtls列表,您可以循环调用InsertOrUpdate

答案 2 :(得分:1)

以下是方法语法。

allCurrentTradeObjects.Select (
            to => to.theseWantMe.Where ( ro => !(ro.Type != to.Type 
                || ro.MaxRent < to.Rent
                || ro.MinRooms > to.Rooms
                || ro.MinSquareMeters > to.SquareMeters
                || ro.MaxPrice < to.Price
                || ro.MinFloors > to.Floors
                || ro.TradeObjectId == to.TradeObjectId
                || ro.TradeObjectId == myTradeObject.TradeObjectId))
               .Select({
                             var rlt =  new RatingListTriangleModel
                              {
                                 To1Id = myTradeObject.TradeObjectId,
                                 To2Id = to.TradeObjectId,
                                 To3Id = ro.TradeObjectId,
                                 T1OnT2Rating = 0,
                                 T2OnT3Rating = 0,
                                 T3OnT1Rating = 0,
                                TotalRating = 0
                            };
                           this.InsertOrUpdate(rlt);
                           return rlt;
                         } ).ToArray();
 this.Save();

答案 3 :(得分:1)

这里有很多答案主张SelectMany(或来自)。这些是为了便于阅读而优化的。回答是因为它们不会改变此操作的N * M嵌套循环性能。

如果两个集合都很大,你就不应该使用这种方法。相反,您应该利用两个集合之间定义良好的关系,以及Enumerable.Join中的散列来将操作减少到N + M.

var myTradeObject = GetThatOneObject();

IEnumerable<RatingListTriangleModel> query =
  from to in allCurrentTradeObjects
 //pre-emptively filter to the interesting objects in the first collection
  where to.TradeObjectId == myTradeObject.TradeObjectId
 //take advantage of hashing in Enumerable.Join - theseWantMe is enumerated once
  join ro in theseWantMe
    on to.Type equals ro.Type
 //remaining matching criteria
  where to.Rent <= ro.MaxRent   //rent is lower than max
    && ro.MinRooms <= to.Rooms   //rooms are higher than min
    && ro.MinSquareMeters <= to.SquareMeters  //area is higher than min
    && to.Price <= ro.MaxPrice     //price is lower than max
    && ro.MinFloors <= to.Floors   // floors are higher than min
    && to.TradeObjectId != ro.TradeObjectId //not same trade object
  select CreateRatingListTriangleModel(myTradeObject, to, ro);

foreach(RatingListTriangleModel row in query)
{
  this.InsertOrUpdate(row);
}
this.Save();

答案 4 :(得分:0)

为了提高可读性,我将首先重构复杂的条件并将其移动到一个简洁的小函数(它也可以是对象的方法)

private bool IsMatchingTradeObject (TradeObject to, SomeOtherObject ro, int TradeObjectId)
{
  return ro.Type == to.Type &&
                  ro.MaxRent >= to.Rent &&
                  ro.MinRooms <= to.Rooms &&
                  ro.MinSquareMeters <= to.SquareMeters &&
                  ro.MaxPrice >= to.Price &&
                  ro.MinFloors <= to.Floors &&
                  ro.TradeObjectId != to.TradeObjectId &&
                  ro.TradeObjectId != TradeObjectId;
}

其次我会对RatingListTriangleModel的创建和初始化做同样的事情,即将其移动到一个小方法并给它一个有意义的名称。

private RatingListTriangleModel CreateModel(TradeObject to, SomeOtherObject ro, int TradeObjectId)
{
  return new RatingListTriangleModel
    {
        To1Id = myTradeObject.TradeObjectId,
        To2Id = to.TradeObjectId,
        To3Id = ro.TradeObjectId,
        T1OnT2Rating = 0,
        T2OnT3Rating = 0,
        T3OnT1Rating = 0,
        TotalRating = 0
    };

剩下的代码更容易阅读

   foreach (var to in allCurrentTradeObjects)
     foreach (var ro in theseWantMe)
       if (IsMatchingTradeObject(to, ro, myTradeObject.TradeObjectId))
         this.InsertOrUpdate(CreateModel(to, ro, myTradeObject.TradeObjectId));

this.Save();

将此转换为LINQ非常简单:

allCurrentTradeObjects.Select (
  to => to.Where ( 
    ro => IsMatchingTradeObject (to, ro, myTradeObject.TradeObjectId)
    )
  ).Select(
     {
       this.InsertOrUpdate(CreateModel(to, ro, myTradeObject.TradeObjectId));
       return null;
     } 
  );
 this.Save();

然而,foreach循环似乎更容易阅读。