tags:

views:

121

answers:

4

I want to add two lists of a numeric type such that addedList[x] = listOne[x] + listTwo[x]

The output of the list needs to be a Generic.IEnumerable that I can use in future linq queries.

While I was able to do it using the code below, I can't help but feel like there must be a better way. Any ideas?

List<int> firstList = new List<int>(new int[] { 1, 3, 4, 2, 5, 7, 2, 5, 7, 8, 9, 0 });
List<int> secondList = new List<int>(new int[] { 4, 6, 8, 3, 1, 5, 9, 3, 0 });

int findex = 0;

ILookup<int, int> flookup = firstList.ToLookup(f =>
                            {
                               int i = findex;
                               findex++; 
                               return i;
                               }, p => p);  

var listsAdded = from grp in flookup
                 select grp.First() + secondList.ElementAtOrDefault(grp.Key);

foreach (int i in listsAdded)
  Console.WriteLine(i);
+1  A: 
var res = list.Concat(list1); 

concatenates two lists, including eventual duplicates.

var res = list.Union(list1);

concatenates two lists, providing a result without duplicates.

Obalix
+2  A: 
var result = 
   from i in 
        Enumerable.Range(0, Math.Max(firstList.Count, secondList.Count))
   select firstList.ElementAtOrDefault(i) + secondList.ElementAtOrDefault(i);
Artem Govorov
This is exactly what I was looking for. Thanks Artem.
Audie
Warning - This approach may be terribly inefficient - especially for lists of any significant size. Each call to `ElementAtOrDefault` may need to iterate through the list from the beginning. Combining two lists of 100 elements might incur the cost of iterating 10,100 elements rather than just 200. For 1000 elements it becomes 1,001,000 elements. And it just gets worse quickly from there...
Enigmativity
@Enigmativity, this is not true in given context. Considering that ElementAtOrDefault is called for list (IList<T> in general) - size of the list doesn't really matter because what ElementAtOrDefault(index) does internally is calling list[index] that accesses list internal array by index, so the access is O(1) and not iterating anything like you're saying. ElementAtOrDefault may behave like you've indicated for those IEnumerable<TSource> that are not of IList<TSource> type (but this is not the case of this question).
Artem Govorov
@Artem - I agree with you. Please note that I said "...may need to iterate..." and not "does need to iterate". The original question gave an example using `IList<>`, but nonetheless the `ElementAtOrDefault` is an extension method of `IEnumerable<>` hence why I thought my warning was valid. Do you not agree?
Enigmativity
@Enigmativity, I agree that your warning is valid for IEnumerable<>, I just mentioned that your warning "is not true in given context". You did say "...may need to iterate the LIST...", but didn't clarify when and why so I decided to give some clarification.
Artem Govorov
+3  A: 

It sounds like you want a function like this:

public static IEnumerable<int> SumIntLists( 
    this IEnumerable<int> first, 
    IEnumerable<int> second) 
{
    using(var enumeratorA = first.GetEnumerator()) 
    using(var enumeratorB = second.GetEnumerator()) 
    { 
        while (enumeratorA.MoveNext()) 
        {
            if (enumeratorB.MoveNext())
                yield return enumeratorA.Current + enumeratorB.Current;
            else
                yield return enumeratorA.Current;
        }
        // should it continue iterating the second list?
        while (enumeratorB.MoveNext())
            yield return enumeratorB.Current;
    } 
} 
Gabe
Thank you Gabe. This ia an excellent example of an extension that would solve my problem. I hoped to be able to do this without writing an extension, as Artem did. Your extension example was instructive though, and I liked the way you handled the differing list lengths. Thanks for your help.
Audie
+3  A: 

What you're looking for is a Zip method. This method allows you to combine to lists of equal length into a single list by applying a projection.

For example

var sumList = firstList.Zip(secondList, (x,y) => x + y).ToList();

This method was added to the BCL in CLR 4.0 (Reference). It's fairly straight forward to implement though and many versions are available online that can be copied into a 2.0 or 3.5 application.

JaredPar
How does Zip handle different-sized sequences?
Gabe
@gabe, different implementations take different routes. The BCL version will ignore the extra elements on the longer list but it's fairly easy to write a different method which extends the list.
JaredPar