Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# aggregating attributes in a list

Tags:

c#

linq

I have an object like as shown below

public class SampleObject
{
    public int MsfId { get; set; }
    public List<string> PgId { get; set; }
    public List<string> DcId { get; set; }
}

In the above aggregation of the PgId values grouped by MsfId. Same is the case with DcId as well.

For example:

MsfId: 100
PgId: "abc"
DcId: "123"

MsfId: 100
PgId: "def"
DcId: "456"

MsfId: 100
PgId: "ghi"
DcId: "789"

MsfId: 101
PgId: "abc"
DcId: "123"

How to write a LINQ query to aggregate this and create a list of SampleObjects like below?

MsfId: 100
PgId: "abc", "def", "ghi"
DcId: "123", "456", "789"

MsfId: 101
PgId: "abc"
DcId: "123"
like image 245
SteelBird82 Avatar asked Jun 01 '16 07:06

SteelBird82


4 Answers

Try aggregation like this:

var result = col.GroupBy(x => x.MsfId)
                .Select(x => new SampleObject {
                       MsfId = x.Key, 
                       PgCodes = x.Select(t=>t.PgId).ToList(),
                       DcCodes = x.Select(t=>t.DcId).ToList()
                });
like image 104
Maksim Simkin Avatar answered Oct 13 '22 03:10

Maksim Simkin


Scenario 1

public class SampleObject
{
    public int MsfId { get; set; }
    public List<string> PgId { get; set; }
    public List<string> DcId { get; set; }
}

Scenario 2

public class SampleObjectSource
{
    public int MsfId { get; set; }
    public string PgId { get; set; }
    public string DcId { get; set; }
}

Scenario 1 Answer

   var collection = new List<SampleObject>();
   var result = collection.GroupBy(y => y.MsfId)
                    .Select(y => new SampleObject
                    {
                        MsfId = y.Key,
                        PgId = y.SelectMany(g => g.PgId).Distinct().ToList(),
                    }).ToList();

Scenario 2

var collection2 = new List<SampleObjectSource>();
var result1 = collection2.GroupBy(y => y.MsfId)
              .Select(y => new SampleObject
              {
                  MsfId = y.Key,
                  PgId = y.Select(h => h.PgId).Distinct().ToList(),
              }).ToList();

Update : Please see the dotnetfiddle

like image 45
Eldho Avatar answered Oct 13 '22 04:10

Eldho


You need to group the items with a query. This linq grouping will create a collection of collections(with a key)

I just made a full working example:

// The class to start with
public class SampleObjectSource
{
    public int MsfId { get; set; }
    public string PgId { get; set; }
    public string DcId { get; set; }
}

// the result class
public class SampleObject
{
    public int MsfId { get; set; }
    public List<string> PgId { get; set; }
    public List<string> DcId { get; set; }
}

// for example:
public class Example
{
    public Example()
    {
        // create a list that contains the items.
        var list = new List<SampleObjectSource>
        {
            new SampleObjectSource { MsfId= 100, PgId=  "abc", DcId= "123" },
            new SampleObjectSource { MsfId= 100, PgId=  "def", DcId= "456" },
            new SampleObjectSource { MsfId= 100, PgId=  "ghi", DcId= "789" },
            new SampleObjectSource { MsfId= 101, PgId=  "abc", DcId= "123" },
        };

        // the linq query that does the grouping.
        var query = from item in list
                    // group the items by MsfId
                    group item by item.MsfId into itemgroup
                    // create the new class and initialize the properties
                    select new SampleObject
                    {
                        // the grouping item is the .Key (in this case MsfId)
                        MsfId = itemgroup.Key,
                        // the itemgroup is a collection of all grouped items, so you need to select the properties you're interrested in.
                        DcId = itemgroup.Select(i => i.DcId).ToList(),
                        PgId = itemgroup.Select(i => i.PgId).ToList()
                    };

        // show the results in the Output window.
        foreach (var item in query)
        {
            Trace.WriteLine($"MsfId: {item.MsfId}");
            // some trick to format a list of strings to one string
            Trace.WriteLine($"PgId: {string.Join(", ", item.PgId.Select(s => Quote(s)))}");
            Trace.WriteLine($"DcId: {string.Join(", ", item.DcId.Select(s => Quote(s)))}");
            Trace.WriteLine("");
        }
    }

    // this method will surround the passed string with quotes.
    private string Quote(string item)
    {
        return "\"" + item + "\"";
    }
}

results:

MsfId: 100
PgId: "abc", "def", "ghi"
DcId: "123", "456", "789"

MsfId: 101
PgId: "abc"
DcId: "123"
like image 3
Jeroen van Langen Avatar answered Oct 13 '22 04:10

Jeroen van Langen


Do it all with one GroupBy using the appropriate overload. Working Fiddle Here.

Note the use of SelectMany to concatenate the grouped collections into one.

var result = sampleObjects
    .GroupBy(
        o => o.MsfId,
        (k, g) => new SampleObject
            {
                MsfId = k,
                PgId = g.SelectMany(p => p.PgId).ToList(),
                DcId = g.SelectMany(p => p.DcId).ToList()
            });

If you want to remove duplicates from the collections consider Distinct() e.g.

var result = sampleObjects
    .GroupBy(
        o => o.MsfId,
        (k, g) => new SampleObject
            {
                MsfId = k,
                PgId = g.SelectMany(p => p.PgId).Distinct().ToList(),
                DcId = g.SelectMany(p => p.DcId).Distinct().ToList()
            });
like image 2
Jodrell Avatar answered Oct 13 '22 04:10

Jodrell