Sakkle
Sakkle

Reputation: 1944

Replacing foreach with LINQ query

I have the following code in one of my methods:

foreach (var s in vars)
{
    foreach (var type in statusList)
    {
        if (type.Id == s)
        {
            Add(new NameValuePair(type.Id, type.Text));
            break;
        }
    }
}

This seems sort of ineffective to me, and I was wondering if there was a way to substitute at least one of the foreaches wih a LINQ query. Any suggestions?

EDIT: vars is an array of strings and the Add method adds an item to a CSLA NameValueList.

Upvotes: 3

Views: 24487

Answers (6)

Amy B
Amy B

Reputation: 110071

I don't see an answer with a group join, so here's one:

var result = vars
  .GroupJoin
  (
    statusList,
    s => s,
    type => type.Id,
    (s, g) => g.Any() ? g.First() : null
  )
  .Where(type => type != null)
  .Select(type => new NameValuePair(type.Id, type.Text));

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500055

EDIT: I hadn't noticed the break; before

If there could be more than one type with the relevant ID, then you need to use FirstOrDefault as per Keith's answer or my second code sample below.

EDIT: Removing "multi-from" version as it's unnecessarily inefficient assuming equality/hashcode works for whatever the type of type.Id is.

A join is probably more appropriate though:

var query = from s in vars
            join type in statusList on s equals type.Id
            select new NameValuePair(type.Id, type.Text);

foreach (var pair in query)
{
    Add(pair);
}

You might want to make an AddRange method which takes an IEnumerable<NameValuePair> at which point you could just call AddRange(query).

Alternatively, you can use a LookUp. This version makes sure it only adds one type per "s".

var lookup = types.ToLookup(type => type.Id);
foreach (var s in vars)
{
    var types = lookup[s];
    if (types != null)
    {
        var type = types.First(); // Guaranteed to be at least one entry
        Add(new NameValuePair(type.Id, type.Text));
    }
}

The benefit of this is that it only goes through the list of types once to build up a dictionary, basically.

Upvotes: 6

Mehrdad Afshari
Mehrdad Afshari

Reputation: 421978

Something like this:

foreach(var s in vars) {
    var type = statusList.FirstOrDefault(t => t.Id == s);
    if (type != null)
        Add(new NameValuePair(type.Id, type.Text));
}

Or if vars supports ForEach method, this would work too (however I recommend against overLINQifying):

vars.ForEach(s => {
    var type = statusList.FirstOrDefault(t => t.Id == s);
    if (type != null)
       Add(new NameValuePair(type.Id, type.Text));
});

I assumed type is an instance of a reference type.

Upvotes: 2

Arcturus
Arcturus

Reputation: 27055

If your Add method is constructing a list, you might also try:

IEnumarable<NamedValuePair> result = statusList.Where(type => type.Id == s).Select(new NameValuePair(type => type.Id, type.Text));

Upvotes: 2

Jakob Christensen
Jakob Christensen

Reputation: 14956

Bart de Smet has an implementation of an extension ForEach for IEnumerable.

Upvotes: 1

Keith
Keith

Reputation: 155652

Basically:

var types =
    from s in vars
    let type = (
        from tp in statusList
        where tp.Id == s ).FirstOrDefault()
    where type != null
    select new NameValuePair(type.Id, type.Text)

Upvotes: 12

Related Questions