DaveDev
DaveDev

Reputation: 42185

How can I pivot this list of objects?

I have a list of Channels

var blocks = new List<Block>();
foreach (var channel in ChannelsCollection)
{
    var ch = channel.ChannelCollectionItems.Select(item => new Channel
    {
        Id = channel.Id, 
        Delay = item.Delay,
        Trigger = item.Trigger,
        Restore = item.Restore,
    }).ToList();

    blocks.Add(new Block { Id = index++ , Channels = ch});
}

This results in a list of Blocks, each of which contains a list of Channels.

e.g.

Block1          Block2          Block3          Block4
    Channel1        Channel1        Channel1        Channel1
        Val1            Val1            Val1            Val1
        Val2            Val2            Val2            Val2
        Val3            Val3            Val3            Val3
    Channel2        Channel2        Channel2        Channel2
        Val1            Val1            Val1            Val1
        Val2            Val2            Val2            Val2
        Val3            Val3            Val3            Val3
    Channel3        Channel3        Channel3        Channel3
        Val1            Val1            Val1            Val1
        Val2            Val2            Val2            Val2
        Val3            Val3            Val3            Val3

I need to pivot this list so that I have a list of Channels that contain each Block for that channel.

e.g.

Channel1    
    Block1        Block2        Block3        
        Val1            Val1            Val1  
        Val2            Val2            Val2  
        Val3            Val3            Val3  

Channel2
    Block1        Block2        Block3        
        Val1            Val1            Val1  
        Val2            Val2            Val2  
        Val3            Val3            Val3            

Channel3
    Block1        Block2        Block3        
        Val1            Val1            Val1  
        Val2            Val2            Val2  
        Val3            Val3            Val3        

Is there a quick way, (e.g. with LINQ) to do this?

Edit

Channel and Block are defined as:

public class Channel
{
    public int? Id { get; set; }
    public string Delay { get; set; }
    public string Trigger { get; set; }
    public string Restore { get; set; }
}

public class Block
{
    public int Id { get; set; }
    public List<Channel> Channels { get; set; }
}

Upvotes: 2

Views: 1741

Answers (3)

DaveDev
DaveDev

Reputation: 42185

This turned out to be the solution:

public class ChannelRowCell
{
    public string Name { get; set; }
    public string Delay { get; set; }
    public string Trigger { get; set; }
    public string Restore { get; set; }
}

public class ChannelRow
{
    public int Id { get; set; }
    public List<ChannelRowCell> Cells { get; set; }
}

public static class DigiChannelsExtensions
{

    public static List<ChannelRow> GetRows(this List<DigiChannelsVM> digiChannelsVms)
    {
        var blocks = (from vm in digiChannelsVms
                      let ch = vm.ChannelCollectionItems.Select(item => new 
                      {
                          vm.Id,
                          Type = item.Types.EnumDataItems.FirstOrDefault(xx => xx.IsSet).Description,
                          Delay = item.Delay.Value.ToString(),
                          Trigger = item.Trigger.Value == 0 ? "+" : "-",
                          Restore = item.Restore.GetSelectedEnumItems().FirstOrDefault().Description == "Restore" ? "Y" : "N",
                      }).ToList()
                      select new { Id = vm.Id.Value, Channels = ch }).ToList();

        var channelRows = new List<ChannelRow>();
        //for (var i = 0; i < blocks[0].Channels.Count; i++)
        for (var i = 0; i < blocks[0].Channels.Count; i++)
        {
            var channelRow = new ChannelRow { Cells = new List<ChannelRowCell>(), Id = i+1 };
            foreach (var cc in blocks.Select(block => block.Channels[i]))
            {
                channelRow.Cells.Add(new ChannelRowCell
                {
                    Delay = cc.Delay,
                    Name = cc.Type,
                    Restore = cc.Restore,
                    Trigger = cc.Trigger,
                });
            }

            channelRows.Add(channelRow);
        }

        return channelRows;
    }
}

Upvotes: 0

Rawling
Rawling

Reputation: 50114

Try something like

var channels = blocks
    .SelectMany(b => b.Channels.Select(c => new { b, c }))
    .GroupBy(p => p.c.Id)
    .Select(g => new { Channel = g.First().c, Blocks = g.Select(p => p.b) });

As others have pointed out, your Channel class doesn't have anywhere to store a sequence of Blocks, so this returns a sequence of anonymous objects with a Channel called Channel and an IEnumerable<Block> called Blocks.

Note that it also compares Channels by Id, since they don't appear to be comparable otherwise. If your Channels are inconsistent between Blocks, this just takes the Channel from the first Block.

Upvotes: 2

Alex Filipovici
Alex Filipovici

Reputation: 32561

Try this:

var r = blocks
    .GroupBy(b => b.Channels.Select(c => c))
    .Select(g => new { Channel = g.Key, Blocks = g.Select(b1 => b1) });

Upvotes: 1

Related Questions