Sagar S.
Sagar S.

Reputation: 193

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.

Upvotes: 1

Views: 12491

Answers (4)

Nowshath
Nowshath

Reputation: 842

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 
                 };

Upvotes: 0

C&#233;dric Bignon
C&#233;dric Bignon

Reputation: 13022

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();

Upvotes: 2

svick
svick

Reputation: 244948

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] }
                 }
             };

Upvotes: 1

Ykok
Ykok

Reputation: 1415

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.

Upvotes: 1

Related Questions