Konrad Kokosa
Konrad Kokosa

Reputation: 16878

Unzip objects collection into arrays of its properties

Let's assume that there is some class:

public class SomeClass
{
    public string A;
    public int B;
    public double C;
}

and we have a collection of it:

var list = new List<SomeClass>() 
{ 
    new SomeClass() { A = "A", B = 2, C = 4.0 }, 
    new SomeClass() { A = "B", B = 6, C = 8.0 } 
};

How can we write in the most efficient way the transform of this collection into three arrays (or list of arrays) of values of A, B and C respectively? In this case it would be:

var Ar = new[] { "A", "B" };
var Br = new[] { 2, 6 };
var Cr = new[] { 4.0, 8.0 };

So far I have working LINQ query:

var lists = list.SelectMany(c => new object[] { c.A, c.B, c.C })
                .Select((v, i) => new { Index = i, Object = v})
                .GroupBy(g => g.Index % 3, g => g.Object, (k,v) => v.ToArray())
                .ToList();

but I wonder if I'm not missing something obvious and if there is possibility to do it clever, for example without the need for creating temporary subcollections within SelectMany.

Edit: my question is rather from theoretical point of view - if it can be done in single LINQ query (for example as a part of another, more complex single query).

Upvotes: 1

Views: 276

Answers (1)

MarcinJuraszek
MarcinJuraszek

Reputation: 125620

Why don't you do this the simplest possible way? I think that 3 separated queries should be more efficient then SelectMany+GroupBy solution:

var Ar = list.Select(x => x.A).ToList();
var Br = list.Select(x => x.B).ToList();
var Cr = list.Select(x => x.C).ToList();

It requires only 3 iterations over list collection. With GroupBy you have only one iteration but the source collection is 3 times longer then list, so there is no difference (because of SelectMany) with additional GetHashCode calls to make the grouping.

Update

Single line version of the same approach:

var items = new [] {
    list.Select(x => x.A).ToList(),
    list.Select(x => x.B).ToList(),
    list.Select(x => x.C).ToList()
}

Update 2

If you don't want to use SelectMany, you can use Concat instead. However, it would require change in GroupBy as well:

var lists = list.Select(c => c.A)
                .Concat(list.Select(c => c.B))
                .Concat(list.Select(c => c.C))
                .Select((v, i) => new { Index = i, Object = v})
                .GroupBy(g => g.Index / list.Count, g => g.Object, (k,v) => v.ToArray())
                .ToList();

Upvotes: 4

Related Questions