Patrick
Patrick

Reputation: 2577

How to replace nested loops with LINQ - in a clean, manageable manner

Codewise, what it the cleanest way to do this using linq? Below, I have a crude example where I want to find a matching class instance based on name.

class item
{
   string name {get;set;}
   int identifier {get;set;}
}

void DoSomething()
{
  List<item> List1 = GetSampleItems();
  List<item> List2 = GetOtherSampleItems();

  for(int a=0;a<List1.count;a++)
  {
     for(int b=0;b<List2.count;b++)
     {
         if(List1[a].identifier == List2[b].identifier)
         { 
            List1[a].name = List2[b].name;
         }
     }
  }
}

Upvotes: 6

Views: 2251

Answers (3)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476624

A better way is using a Dictionary<TK,TV>:

Dictionary<int,item> l2dic = List2.ToDictionary(x => x.identifier);
item itm;
List1.ForEach(x => {
    if(l2dic.TryGetValue(x.identifier,out itm)) {
        x.name = itm.name;
    }
});

Or as @Rawling says, use a foreach loop instead:

Dictionary<int,item> l2dic = List2.ToDictionary(x => x.identifier);
item itm;
foreach(item x in List1) {
    if(l2dic.TryGetValue(x.identifier,out itm)) {
        x.name = itm.name;
    }
}

Ideone demo (with slight modifications to your item class).

This runs on average in linear time whereas your approach runs in quadratic time.

The assumption is however that the identifiers are unique: no two elements in the same list can have the same identifier.

A concluding note is that variables in general start with a lowercase character so list1 and list2 whereas classes and properties start with a capital one (this Item, Identifier and Name).

Upvotes: 4

Ric
Ric

Reputation: 13248

This should work:

var query = from l1 in List1
            join l2 in List2 on l1.identifier equals l2.identifier
            select new 
            {
                l1values = l1,
                l2Name = l2.name
            };

foreach(var item in query)
    item.l1Values.name = item.l2Name;

Upvotes: 4

D Stanley
D Stanley

Reputation: 152556

Linq is for querying, not updating, so you'll still need to loop through the results to make the changes, but you can join to match up the two lists like so:

var query = from l1 in List1
            join l2 in List2
                on l1.identifier equals l2.identifier 
            select new {l1, l2};

Now loop through the query to update the l1 items:

foreach(var item in query)
    item.l1.name = item.l2.name;

As a side note, there's nothing wrong with the way you're doing it (other than you could break out of the inner loop if a match is found). If you understand how it works and the performance is acceptable, there's no compelling reason to change it.

Upvotes: 10

Related Questions