PiousVenom
PiousVenom

Reputation: 6908

Use LINQ to populate a single list from two other lists

Right now, I have 2 lists(_pumpOneSpm and _pumpTwoSpm) that are the result of a database query. Now, I've been using LINQ to populate lists for me to work with for calculations. Something similar to this:

var spmOne = _pumpOneSpm.Where((t, i) => _date[i].Equals(date) &&
            DateTime.Compare(_time[i], DateTime.Parse(start)) > 0 && 
            DateTime.Compare(_time[i], DateTime.Parse(end)) < 0).ToList();

This has been fine, because I've been pulling data from one list to add specific data into another list. But now, I need pull data from two lists to add to one list. I'm curious if there is a way to do this with LINQ, or if I should just iterate through with a for loop? Here's what I've got with the for loop:

        for (var i = 0; i < _pumpOneSpm.Count; i++)
        {
            if (_date[i].Equals(date))
            {
                if (DateTime.Compare(_time[i], DateTime.Parse(start)) > 0)
                {
                    if (DateTime.Compare(_time[i], DateTime.Parse(end)) < 0)
                    {
                        spmOne.Add(_pumpOneSpm[i]);
                        spmOne.Add(_pumpTwoSpm[i]);
                    }
                }
            }
        }

This works, and does what I want. But I'm just trying to make it a bit more efficient and faster. I could use two lines similar to the first code block for each list, but that means I'm iterating through twice. So, to reiterate my question, is it possible to use a LINQ command to pull data from two lists at the same time to populate another list, or should I stick with my for loop? Thanks for any and all help.

EDIT

I failed to mention, that the purpose of the function utilizing these steps is to fine the MAX of _pumpOneSpm & _pumpTwoSpm. Not the max of each, but the max between them. Thus initially I was adding them into a single list and just calling spmOne.Max(). But with two LINQ queries like above, I'm running an if statement to compare the two maxes, and then return the greater of the two. So, technically they don't NEED to be in one list, I just thought it'd be easier to handle if they were.

Upvotes: 1

Views: 2553

Answers (5)

Risky Martin
Risky Martin

Reputation: 2521

If you want to avoid using indexes you can Zip everything together.

spmOne = _pumpOneSpm
          .Zip(_pumpTwoSpm, (pump1, pump2) => new { pump1, pump2 } )
          .Zip(_date, (x, pumpDate) => new { x.pump1, x.pump2, date = pumpDate })
          .Zip(_time, (x, time) => new { x.pump1, x.pump2, x.date, time })
          .Where(x => x.date.Equals(date) &&
                      DateTime.Compare(x.time, DateTime.Parse(start)) > 0 &&
                      DateTime.Compare(x.time, DateTime.Parse(end)) < 0)
          .SelectMany(x => new [] { x.pump1, x.pump2 })
          .ToList();

There is one small difference in functionality: if any of the sequences are shorter than _pumpOneSpm, the resulting sequence will be the length of the shortest sequence, and no exception will be thrown about an index being out of range.

Upvotes: 0

Risky Martin
Risky Martin

Reputation: 2521

You can achieve similar functionality to the for loop with Enumerable.Range. SelectMany then allows you to add multiple items each iteration and then flatten the resulting sequence.

spmOne = Enumerable
             .Range(0, _pumpOneSpm.Count)
             .Where(index => _date[index].Equals(date) &&
                     DateTime.Compare(_time[index], DateTime.Parse(start)) > 0 &&
                     DateTime.Compare(_time[index], DateTime.Parse(end)) < 0)
             .SelectMany(index => new [] { _pumpOneSpm[index], _pumpTwoSpm[index] })
             .ToList();

Here is the query syntax version:

spmOne = (from index in Enumerable.Range(0, _pumpOneSpm.Count)
          where _date[index].Equals(date) &&
                DateTime.Compare(_time[index], DateTime.Parse(start)) > 0 &&
                DateTime.Compare(_time[index], DateTime.Parse(end)) < 0
          from pumpSpm in new [] { _pumpOneSpm[index], _pumpTwoSpm[index] }
          select pumpSpm).ToList();

Upvotes: 0

Erre Efe
Erre Efe

Reputation: 15557

So, to reiterate my question, is it possible to use a LINQ command to pull data from two lists at the same time to populate another list, or should I stick with my for loop?

Merge the lists:

var mergedList = list1.Union(list2).ToList();

Optionally if you want, then filter:

var filtered = mergedList.Where( p => ... filter ... );

And for the Max:

var max = filtered.Max();

NOTE:

As OP says there's no need from order not for not-duplicates so Union will be OK.

Upvotes: 2

Servy
Servy

Reputation: 203814

So the simplest way to do this would be something like this:

var resultList = list1.Concat(list2).ToList();

Note that this will be slightly different than your for loop (the order of the items won't be the same). It will have all items from one list followed by all items from another, rather than having the first of the first, the first of the second, etc. If that's important you could still do it with LINQ, but it would take a few extra method calls.

You've mentioned performance; it's worth noting that you aren't going to get significantly better, performance wise, than your for loop. You could get code that's shorter; code that's more readable, and that will perform about the same. There simply isn't room to improve very much more though in terms of runtime speed.

It's also worth noting that you can combine the two method; it's not even a bad idea. You can use LINQ to filter the two input sequences (using Where) and then foreach over the results to add them to the list (or use AddRange).

var query1 = _pumpOneSpm.Where((t, i) => _date[i].Equals(date) &&
            DateTime.Compare(_time[i], DateTime.Parse(start)) > 0 && 
            DateTime.Compare(_time[i], DateTime.Parse(end)) < 0);
var query2 = ...
List<T> results = new List<T>(query1);
results.AddRange(query2);

Upvotes: 2

Gabber
Gabber

Reputation: 5452

You can use the SelectMany method

http://msdn.microsoft.com/en-us/library/bb548748.aspx

Upvotes: 0

Related Questions