简体   繁体   中英

Nested ForEach loop to Linq

I am trying to convert below nested for each loop into Linq. However I am still unable to do it successfully.

var objAct = new List<InformaticsBenchmarkSummary>();
foreach (var item in op)
{
    foreach (var lstTp5 in lstTopFive)
    {
        if (item.UnitOfOperations.ContainsKey(lstTp5.SystemID))
        {
            var objIbm = new InformaticsBenchmarkSummary();
            objIbm.CompanyId = item.CompanyId;
            objIbm.CompanyName = item.CompanyName;
            objIbm.LocationId = item.LocationId;
            objIbm.LocationName = item.LocationName;
            objIbm.UnitOfOperations.Add(lstTp5.SystemID, 
                                        item.UnitOfOperations[lstTp5.SystemID]);
            objAct.Add(objIbm);
        }
    }
}

Where UnitOfOperations is of type Dictionary<int,string>() ;
op is again List<InformaticsBenchmarkSummary>()
lstTopFive is List<int>()

I tried, something like this but was unsuccessful syntactically

var output = from item in op
from lstTp5 in lstTopFive
where item.UnitOfOperations.ContainsKey(lstTp5.SystemID)
let v = new InformaticsBenchmarkSummary()
{
     CompanyId = item.CompanyId,
     CompanyName = item.CompanyName,
     LocationId = item.LocationId,
     LocationName = item.LocationName
}
.UnitOfOperations.Add(lstTp5.SystemID, item.UnitOfOperations[lstTp5.SystemID])
select v;

Nested loop works perfectly but I think, linq on this will increase performance. Appreciate any help.

It is impossible in a Linq query-syntax to use the UnitOfOperations.Add in the select. But you can do it using Method Chain and a SelectMany method:

var objAcu = (op.SelectMany(item => lstTopFive, (item, lstTp5) => new { item, lstTp5 })  // <- Bad readability
                .Where(t => t.item.UnitOfOperations.ContainsKey(t.lstTp5.SystemID))
                .Select(t =>
                        {
                            var objIbm = new InformaticsBenchmarkSummary
                            {
                                CompanyId = t.item.CompanyId,
                                CompanyName = t.item.CompanyName,
                                LocationId = t.item.LocationId,
                                LocationName = t.item.LocationName
                            };
                            objIbm.UnitOfOperations.Add(t.lstTp5.SystemID, t.item.UnitOfOperations[t.lstTp5.SystemID]);
                            return objIbm;
                        })).ToList();

If the property UnitOfOperations has a public set , in this case, you can use the query-syntax.

How do you replace 1 foreach ? By a from ... in ...

Then, to replace 2 foreach , use 2 from ... in ...

var objAct = (from item in op  // First foreach loop
              from lstTp5 in lstTopFive  // Second foreach loop
              where item.UnitOfOperations.ContainsKey(lstTp5.SystemID)
              select new InformaticsBenchmarkSummary
              {
                  CompanyId = item.CompanyId,
                  CompanyName = item.CompanyName,
                  LocationId = item.LocationId,
                  LocationName = item.LocationName,
                  UnitOfOperations = { { lstTp5.SystemID, item.UnitOfOperations[lstTp5.SystemID] } }
              }).ToList();

But I doubt it will increase performance in such an operation.

Anyway, I don't understand what you try to achieve. Because in all items of the output will have one and only one element in the dictionary UnitOfOperations . Is it really what you want to do?

UPDATE

List<int> systemIdTop5 = lstTopFive.Select(tp5 => tp5.SystemID).ToList();
var objAct = (from item in op
              select new InformaticsBenchmarkSummary
              {
                  CompanyId = item.CompanyId,
                  CompanyName = item.CompanyName,
                  LocationId = item.LocationId,
                  LocationName = item.LocationName,
                  UnitOfOperations = systemIdTop5.Intersect(item.UnitOfOperations.Keys)
                                                 .ToDictionary(systemId => systemId, systemId => item.UnitOfOperations[systemId])
              }).ToList();

You're close, but you can't just use a void returning method in LINQ query like that. (And if it wasn't void -returning, then v would be the result of Add() , which would be most likely wrong.)

If you wanted to create a new Dictionary for UnitOfOperations , you could set it the same way as other properties. But if you can't do that (probably because UnitOfOperations has a private setter) or you don't want to (because UnitOfOperations is initialized to some value that you want to keep), you can use a lesser known feature of C#: collection initializer inside object initializer:

UnitOfOperations = { { lstTp5.SystemID, item.UnitOfOperations[lstTp5.SystemID] } }

The effect of this code is the same as if you wrote:

createdObject.UnitOfOperations.Add(lstTp5.SystemID, item.UnitOfOperations[lstTp5.SystemID]);

The only difference is that it's not a statement, it's part of an expression, which means you can use it in a LINQ query.

The whole query would then be:

var output = from item in op
             from lstTp5 in lstTopFive
             where item.UnitOfOperations.ContainsKey(lstTp5.SystemID)
             select new InformaticsBenchmarkSummary()
             {
                 CompanyId = item.CompanyId,
                 CompanyName = item.CompanyName,
                 LocationId = item.LocationId,
                 LocationName = item.LocationName,
                 UnitOfOperations =
                 {
                     { lstTp5.SystemID, item.UnitOfOperations[lstTp5.SystemID] }
                 }
             };

As far as I can tell it is your UnitOfOperations that you're having difficulty with. If it is initialized in the constructor you can use this:

            var output = from item in op
                     from lstTp5 in lstTopFive
                     where item.UnitOfOperations.ContainsKey(lstTp5.SystemID)
                     select
                         new InformaticsBenchmarkSummary()
                             {
                                 CompanyId = item.CompanyId,
                                 CompanyName = item.CompanyName,
                                 LocationId = item.LocationId,
                                 LocationName = item.LocationName,
                                 UnitOfOperations = { { lstTp5.SystemID, item.UnitOfOperations[lstTp5.SystemID] } }
                             };

The result is an IEnumerable , if you want it as a list, call output.ToList() .

Two side notes:

  1. I don't believe this will be any quicker. It is still a inner loop.
  2. This might produce almost duplicate items in the result (different UnitOfOperations ), but I guess that is desired. In the worst case scenario all items in op have a UnitOfOperations that contain all the SystemID in lstTopFive giving us a total of op.Count()*lstTopFive.Count() items in the output .

May this help U :

var output = from item in op
                 join lstTp5 in lstTopFive on item.UnitOfOperations.Key equals lstTp5.SystemID
                 select new InformaticsBenchmarkSummary
                 {
                     CompanyId = item.CompanyId,
                     CompanyName = item.CompanyName,
                     LocationId = item.LocationId,
                     LocationName = item.LocationName,
                     UnitOfOperations = item.UnitOfOperations 
                 };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM