5

I have a class that has two integers in it, for example A and B:

public class MyClass {
   public int A { get; set; }
   public int B { get; set; }
   ...other stuff...
}

I have a MyCollection of type ObservableCollection<MyClass> in code, and have a need to get an IEnumerable<int> of ALL the values -- both A's and B's -- together in one list.

I have figured out how to do it with quite verbose code (significantly simplified to be only one level below for example purposes, but actually 3 levels of "from" calls and selecting values from within nested lists):

IEnumerable<int> intsA=
    (from x in MyCollection
            select x.A);
IEnumerable<int> intsB =
    (from x in MyCollection
            select x.B);
IEnumerable<int> allInts = intsA.Concat(intsB);

It seems like there should be a way to select both variables at the same time into the same IEnumerable<int>. Obviously below doesn't work, but I'd love something like

IEnumerable<int> allInts = (from x in MyCollection select x.A, x.B);

Does something like this exist that is more elegant than what I have above?

I found how to select multiple values into an anonymous type here, but that doesn't make it into the same IEnumerable and still requires more code/processing to get the items out.

(BTW, using .NET 4.5.1, if that makes a difference.) Thanks!

Community
  • 1
  • 1
Meredith
  • 173
  • 1
  • 7

2 Answers2

13

You could use SelectMany:

var result = source.SelectMany(x => new[] { x.A, x.B });

But because you'd allocate a new array for each object, I don't know how performance it will be (or maybe you don't care about it that much).

You could declare GetIntValues on your type which would return IEnumerable<int>:

public class MyClass {
   public int A { get; set; }
   public int B { get; set; }
   ...other stuff...

   public IEnumerable<int> GetIntValues()
   {
       yield return A;
       yield return B;
   }
}

And use it like this:

var result = source.SelectMany(x => x.GetIntValues());

But there is still an additional allocation for each element.

MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • I'm guessing the downvoter didn't look closely enough. There's a big difference between `new { x.A, x.B }` and `new[] { x.A, x.B }`, and what you're doing looks right to me. –  Mar 06 '15 at 21:41
  • This is a very standard way of solving the OP's problem – Andro Mar 06 '15 at 21:42
  • Possibly somebody who didn't like the question (not sure why, it seems like a decent question to me) and decided to downvote anybody who dared to answer it. – Matt Burland Mar 06 '15 at 21:43
  • @MattBurland Doubt it, because they didn't downvote all the other answers. – Servy Mar 06 '15 at 21:45
  • @Servy: As of *right now*, every answer has a downvote. The question is also `-2/+2`. – Matt Burland Mar 06 '15 at 21:45
  • @Servy they did originally. – Andro Mar 06 '15 at 21:45
  • @MattBurland Not every answer was correct. I've downvoted incorrect answers, and account for the only downvote. So the person that downvoted this answer, didn't downvote them all. – Servy Mar 06 '15 at 21:46
  • Thanks for the ideas. The class is actually very complex already so I'm not crazy about the GetIntValues(), since I'd essentially be adding it for a single usage in the whole app (where the rest of the class is used all over the place by different layers). I should have written the example more detailed in that its actually ('from x in MyCollection from y in x.Stuff from z in y.MoreStuff select z.A') -- so SelectMany doesn't cover that from what I can tell. I used the other answer from Lucas, but this was very informative for my future reference so thanks! – Meredith Mar 07 '15 at 04:20
6

That's pretty easy indeed:

IEnumerable<int> allInts = MyCollection.Select(i => i.A)
                                       .Concat(MyCollection.Select(i => i.B));

It's equivalent to what you wrote, but less verbose. It's using the lambda syntax instead of query comprehension syntax.

Use it if you want to avoid additional allocations. If you don't care about GC pressure, Marcin's solution is even shorter. Also, this outputs the elements in a different order than his solution.

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
  • Thank you! This got me where I needed. Unfortunately I literally joined the site this week so I don't have enough reputation to mark as answer or upvote yet, but I would if I could (and will later when I can)! – Meredith Mar 07 '15 at 04:23
  • You're welcome. Just so you know, there is no reputation needed to mark an answer as accepted, but you need 15 rep to upvote. Take a look at the [privileges](http://stackoverflow.com/help/privileges) page for more info, and welcome to SO ;) – Lucas Trzesniewski Mar 07 '15 at 08:53