Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simplifying a foreach loop with LINQ (selecting two objects in each iteration)

Tags:

c#

linq

Given the following interface and two classes:

public interface IMyObj
{
    int Id { get; set; }
}

public class MyObj1 : IMyObj
{
    public MyObj1(int id) { Id = id; }
    public int Id { get; set; }
    public override string ToString() => $"{GetType().Name} : {Id}";
}

public class MyObj2 : IMyObj
{
    public MyObj2(int id) { Id = id; }
    public int Id { get; set; }
    public override string ToString() => $"{GetType().Name} : {Id}";
}

And given following logic that uses them:

var numbers = new[] { 1, 5, 11, 17 };

var list = new List<IMyObj>();

foreach (var n in numbers)
{
    // I'd like to simplify this part with LINQ...
    list.Add(new MyObj1(n));
    list.Add(new MyObj2(n));
}

Assert.AreEqual(8, list.Count);

The test passes, and I see inside list exactly what I want - two object instances per a number:

Count = 8
  [0]: {MyObj1 : 1}
  [1]: {MyObj2 : 1}
  [2]: {MyObj1 : 5}
  [3]: {MyObj2 : 5}
  [4]: {MyObj1 : 11}
  [5]: {MyObj2 : 11}
  [6]: {MyObj1 : 17}
  [7]: {MyObj2 : 17}

My question is, how do I simplify the foreach loop logic with LINQ? I'm thinking there might be an elegant way in doing the same with the SelectMany operator perhaps, but I've not been able to produce the same output.

like image 488
Pompair Avatar asked Aug 30 '17 12:08

Pompair


1 Answers

SelectMany is indeed what you want:

var list = numbers.SelectMany(n => new IMyObj[] { new MyObj1(n), new MyObj2(n) })
                  .ToList();

In other words, for each number, create a two-element array, which is then flattened using to SelectMany.

The new IMyObj[] part is required rather than just new[] because type inference can't tell what array type you'd want. If the types were the same, you could do something like:

var list = numbers.SelectMany(n => new[] { new MyObj(n), new MyObj(n) })
                  .ToList();
like image 144
Jon Skeet Avatar answered Oct 17 '22 16:10

Jon Skeet