Andez
Andez

Reputation: 5858

Convert flat object to hierarchy with c#

I've seen similar questions but not something which answers my scenario. So...

Given the types:

public class Flat
{
    public string Group1 { get; set; }
    public string Group2 { get; set; }  
    public int Value { get; set; }
}

public class Group1 
{
    public Name { get; set; }
    public Group2[] Group2 { get; set; }
}

public class Group2
{
    public Name { get; set; }
    public int[] Values;
}

I want to convert the Flat array to the Group1/Group2 hierarchy specified above. The array is as follows:

var flats = new []
    {
        new Flat { Group1 = "G1", Group2 = "G1.1", Value = 12 },
        new Flat { Group1 = "G1", Group2 = "G1.1", Value = 22 },
        new Flat { Group1 = "G1", Group2 = "G1.2", Value = 13 },
        new Flat { Group1 = "G2", Group2 = "G1.1", Value = 14 },
        new Flat { Group1 = "G2", Group2 = "G2.2", Value = 15 },
    };

So the output will be equivalent to:

var result = new []
{
    new Group1 
    { 
        Name = "G1"
        Group2 = [] 
        {
            new Group2 
            { 
                Name = "G1.1" 
                Values = [] { 12, 22 }
            },
            new Group2 
            { 
                Name = "G1.2" 
                Values = [] { 13 }
            },
        }
    },
    new Group1 
    { 
        Name = "G2"
        Group2 = [] 
        {
            new Group2 
            { 
                Name = "G2.1" 
                Values = [] { 14 }
            },
            new Group2 
            { 
                Name = "G2.2" 
                Values = [] { 15 }
            },
        }
    }
}

I have tried and failed using GroupBy:

var result = flats
        .GroupBy(f => f.Group1)
        .Select(f => f.GroupBy(x => x.Group2).Select(y => y.Select(x => x.Value)));

I've been fiddling with this in linqpad but not found a solution. I've been trying anonymous types initially but added the group types.

Is this possible with linq?

Upvotes: 0

Views: 608

Answers (3)

Joas
Joas

Reputation: 2026

This should do the trick. You want to group the results of a group. So group 2 times.

var result = flats.GroupBy(x => x.Group1).Select(group1 => new Group1()
{
    Name = group1.Key,
    Group2 = group1.GroupBy(x => x.Group2).Select(group2 => new Group2()
    {
        Name = group2.Key,
        Values = group2.Select(x => x.Value).ToArray()
    }).ToArray()
});

Upvotes: 1

NetMage
NetMage

Reputation: 26936

You just need to use LINQ to build the groupings at two levels:

var ans = flats.GroupBy(f => f.Group1)
               .Select(fg => new Group1 {
                                Name = fg.Key,
                                Group2 = fg.GroupBy(f => f.Group2).Select(fg2 => new Group2 {
                                                                            Name = fg2.Key,
                                                                            Values = fg2.Select(f => f.Value).ToArray()
                                                                          })
                                                                   .ToArray()
                       })
               .ToArray();

NOTE: In general, it is better to prefer ToList instead of ToArray unless you have a specific need for the Array type (normally interoperability with other code).

Upvotes: 1

PaulF
PaulF

Reputation: 6783

You can perform the following Linq operation :

   Group1 [] res = flats.GroupBy( g1 => g1.Group1, 
      (key1,res1) => new Group1{ Group2 = 
         res1.GroupBy( g2 => g2.Group2, 
             (key2,res2) => new Group2{ Values = res2.Select( v => v.Value).ToArray(), 
             Name = key2}).ToArray(), Name = key1}).ToArray();

First grouping by the Group1 string, the inner group creates an array of Group2 which is initialised by selecting the integer values & creating an array.

Upvotes: 1

Related Questions