What's the C#-idiomatic way for applying an operator across two lists?

Solution 1:

var c = a.Zip(b, (x, y) => x * y);

For the more complex one after your edit:

var c = a.Zip(b, (x, y) => x > y ? x - y : 0);

Note that Zip is an extension method both from Enumerable that acts on IEnumerable<T> and from Queryable that acts on IQueryable<T>, so it is possible that, should the lambda be one a given query provider can deal with, that it could be processed as a SQL query on a database, or some other way other than in-memory in .NET.

Someone mentioned that this was new with 4.0 in the comments. It's not hard to implement for 3.5 yourself:

public class MyExtraLinqyStuff
{
    public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
    {
      //Do null checks immediately;
      if(first == null)
        throw new ArgumentNullException("first");
      if(second == null)
        throw new ArgumentNullException("second");
      if(resultSelector == null)
        throw new ArgumentNullException("resultSelector");
      return DoZip(first, second, resultSelector);
    }
    private static IEnumerable<TResult> DoZip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
    {
      using(var enF = first.GetEnumerator())
      using(var enS = second.GetEnumerator())
        while(enF.MoveNext() && enS.MoveNext())
          yield return resultSelector(enF.Current, enS.Current);
    }
}

For .NET2.0 or .NET3.0 you can have the same, but not as an extension method, which answers another question from the comments; there wasn't really an idiomatic way of doing such things in .NET at that time, or at least not with a firm consensus among those of us coding in .NET then. Some of us had methods like the above in our toolkits (though not extension methods obviously), but that was more that we were influenced by other languages and libraries than anything else (e.g. I was doing things like the above because of stuff I knew from C++'s STL, but that was hardly the only possible source of inspiration)

Solution 2:

Assuming .Net 3.5 with lists of equal length:

var a = new List<int>() { 1, 2, 3 };
var b = new List<int>() { 5, 1, 2 }; 
    
var c = a.Select((x, i) => b[i] * x);

Result:

5

2

6

DotNetFiddle.Net Example

Solution 3:

If you are not using .NET 4.0 here is how to write your own extension method to do a Zip.

static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector) 
{
    using (IEnumerator<TFirst> e1 = first.GetEnumerator())
    using (IEnumerator<TSecond> e2 = second.GetEnumerator())
    {
        while (e1.MoveNext() && e2.MoveNext())
        {
            yield return resultSelector(e1.Current, e2.Current);
        }
    }
}

Solution 4:

For .NET verions without LINQ I would recommend a for loop to accomplish this:

List<int> list1 = new List<int>(){4,7,9};
List<int> list2 = new List<int>(){11,2,3};
List<int> newList = new List<int>();
for (int i = 0; i < list1.Count; ++i)
{
    newList.Add(Math.Max(list1[i], list2[i]));
}

This assumes, of course, the lists are the same size and not changing. If you know the list size ahead of time you could also instantiate it to the correct size then just set the element during the loop.