61

I'm used to doing this (from other languages):

 a = 1, 2, 3;
 b = 5, 1, 2;

 c = a * b;  // c = 5, 2, 6

This takes two lists of equal size and applies a function to their members, one at a time, to get a list of the results. It could be a function as simple as multiplication (above) or something more complex:

 c = b>a ? b-a : 0;  // c = 4, 0, 0

I can think of a few different ways to do this in C#, but I'm not sure how a C#-trained programmer would do it. What's the proper way of going about this in the C# world?

(The only part I'm asking about is where c = f(a,b). I'm familiar with creating lists and accessing their elements.)

8
  • Good question, I would probably just do this with a for loop due to the need for parallel indices. For multiplying by a simple scalar, a Select statement. Commented Jun 3, 2014 at 23:28
  • Look into the ins-and-outs of LINQ for operations such as this, as it provides clean, shorthand notition for selecting, or applying operators to lists. msdn.microsoft.com/en-us/library/bb397933.aspx
    – GEEF
    Commented Jun 3, 2014 at 23:29
  • @VP., write that as an answer, give an example of using LINQ for that purpose, and I'll be happy to upvote it.
    – Joe
    Commented Jun 3, 2014 at 23:31
  • 4
    Also consider Anti-pattern: parallel collections, by Jon Skeet.
    – user
    Commented Jun 4, 2014 at 12:42
  • 1
    @MichaelKjörling, that link is dead, but see Jon Skeet's blog.
    – Joe
    Commented Mar 13, 2015 at 17:21

4 Answers 4

77
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)

13
  • 7
    Important note, Zip is new to .NET 4.0 so if you are targeting 3.5 it will not be available. (but it is not hard to implment yourself as an extension method) Commented Jun 3, 2014 at 23:31
  • 1
    Couldn't remember quick enough that it was Zip. +1 Commented Jun 3, 2014 at 23:31
  • 3
    @DavidG Or Enumerable.Range(1,5).Select(x => x*x) Commented Jun 3, 2014 at 23:34
  • 1
    @Joe not really, but take a quick look at the soure code for it, It would not be hard to add to your own project. Commented Jun 3, 2014 at 23:34
  • 1
    Per one of your last updates about not being able to do extension methods in .NET 2.0 or 3.0, I discovered a neat trick a while ago you can do to add support for extension methods to .NET 2.0 or 3.0. However you need to be using Visual Studio 2008 or newer for it to work. Commented Jun 3, 2014 at 23:51
23

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

2
  • Is Select a Linq function?
    – Joe
    Commented Jun 13, 2014 at 23:13
  • 1
    System.Linq.Select is an extension method found in the assembly System.Core. Commented Jun 13, 2014 at 23:14
19

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);
        }
    }
}
12

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.

10
  • 2
    Now we have all our bases covered, A post 3.5 solution, a 3.5 solution and a pre 3.5 solution. (for the size issue, you can replicate what Zip does by making the for loop for (int i = 0; i < Math.Min(list1.Count, list2.Count); ++i) Commented Jun 3, 2014 at 23:38
  • @ScottChamberlain Which features in your solution aren't available pre-3.5? Commented Jun 3, 2014 at 23:39
  • @BenAaronson Extension methods. But you can put extension method support back in via a neat hack. Commented Jun 3, 2014 at 23:41
  • @ScottChamberlain Extension methods yes, but what LINQ features do you use? Commented Jun 3, 2014 at 23:42
  • 1
    @ErikPhilips good .NET code does not assume it's been called correctly, if it's intended for reuse (a private method only called in a limited number of cases is another matter).
    – Jon Hanna
    Commented Jun 4, 2014 at 0:00

Not the answer you're looking for? Browse other questions tagged or ask your own question.