-1

I have a List as shown below.

var boxes= new List<Box>(){
new Box(){ Id=1, Name="A1", SrNo="A11"},
new Box(){ Id=2, Name="A1", SrNo="A22"},
new Box(){ Id=3, Name="B1", SrNo="B11"},
new Box(){ Id=4, Name="B1", SrNo="B22"},
new Box(){ Id=5, Name="C1", SrNo="C11"},
new Box(){ Id=6, Name="D1", SrNo="D11"}
}

I want to clean the list by merging duplicates by Name so that output looks like this.

var boxes = {
        new Box(){ Id=1, Name="A1", SrNo="A11, A22"},
        new Box(){ Id=3, Name="B1", SrNo="B11, B22"},
        new Box(){ Id=5, Name="C1", SrNo="C11"},
        new Box(){ Id=6, Name="D1", SrNo="D11"}
        }

I understand that I need to do GroupBy but how do I update SrNo so that it appends values from duplicates & remove duplicates rows as well from list ?

4 Answers4

3

Something like

var consolidatedBoxes = boxes 
  .GroupBy(b => b.Name)
  .Select(group => 
    new Box { 
       Id = group.First().Id, 
       Name = group.Key.Name, 
       SrNo = string.Join(", " , group.SelectMany(e => e.SrNo).ToList()) })
        .ToList();

This is untested, I wrote it on my phone. I think it will do what you want, however.

ReRoute
  • 371
  • 1
  • 13
  • It won't work because you're grouping by Id AND Name, rather than just Name. – ProgrammingLlama Oct 17 '20 at 05:09
  • Good catch, I think i have edited to correct solution, still untested though – ReRoute Oct 17 '20 at 05:29
  • 1
    `FirstOrDefault().Id` is strange. `FirstOrDefault` expresses that a group could be empty, and in that case you'd get a NullReferenceException. `First().Id` seems more appropriate here. – jeroenh Oct 17 '20 at 10:32
1

For the following result

Id   Name SrNo
1    A1   A11,A22
3    B1   B11,B22
5    C1   C11
6    D1   D11

Try this code:

class Program
{
    static void Main(string[] args)
    {
        var boxes = new List<Box>(){
            new Box(){ Id=1, Name="A1", SrNo="A11"},
            new Box(){ Id=2, Name="A1", SrNo="A22"},
            new Box(){ Id=3, Name="B1", SrNo="B11"},
            new Box(){ Id=4, Name="B1", SrNo="B22"},
            new Box(){ Id=5, Name="C1", SrNo="C11"},
            new Box(){ Id=6, Name="D1", SrNo="D11"}
        };
        var merged = MergeDuplicates(boxes);

        Console.WriteLine($"{"Id",-4} {"Name",-4} {"SrNo"}");
        foreach (var item in merged)
        {
            Console.WriteLine($"{item.Id,-4} {item.Name,-4} {item.SrNo}");
        }
    }

    static List<Box> MergeDuplicates(List<Box> boxes)
    {
        List<Box> result = new List<Box>();
        foreach (var group in boxes.GroupBy((item) => item.Name))
        {
            string name = group.Key;
            var dupes = group.ToArray();
            var first = dupes.First();
            var collect = string.Join(",", dupes.Select((item) => item.SrNo));
            result.Add(new Box() { Id=first.Id, Name=first.Name, SrNo = collect });
        }
        return result;
    }
}
John Alexiou
  • 28,472
  • 11
  • 77
  • 133
1

Lets assume your Box class is like this.

        class Box
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string SrNo { get; set; }


            public override string ToString()
            {
                return $"Id: {Id}\tName: {Name}\t SrNo: {SrNo}";
            }

        }

If you want to use linq, you can do it like this:

Box[] boxesOrganized =
                boxes
                .GroupBy(grp => grp.Name)    //Create Grouping By Name
                .ToArray()
                .Select(s => new Box()      //Create New Box For Each Grouping                               
            {
                    Id = s.First().Id,      //You Only Want The First Id
                Name = s.First().Name,  //You Only Want the First Name
                SrNo = string.Join(     //Grab The SrNo's And Seperate With A Comma
                        separator: ", ",
                        value: s.Select(s2 => s2.SrNo).ToArray())
                })
                .ToArray();

This will get you the output you were looking for:

enter image description here

Full Code:

        public static void GroupMyBoxes()
        {
            List<Box> boxes =
                new List<Box>(){
                new Box(){ Id=1, Name="A1", SrNo="A11"},
                new Box(){ Id=2, Name="A1", SrNo="A22"},
                new Box(){ Id=3, Name="B1", SrNo="B11"},
                new Box(){ Id=4, Name="B1", SrNo="B22"},
                new Box(){ Id=5, Name="C1", SrNo="C11"},
                new Box(){ Id=6, Name="D1", SrNo="D11"}
            };



            Box[] boxesOrganized =
                boxes
                .GroupBy(grp => grp.Name)    //Create Grouping By Name
                .ToArray()
                .Select(s => new Box()      //Create New Box For Each Grouping                               
                {
                    Id = s.First().Id,      //You Only Want The First Id
                    Name = s.First().Name,  //You Only Want the First Name
                    SrNo = string.Join(     //Grab The SrNo's And Seperate With A Comma
                        separator: ", ",
                        value: s.Select(s2 => s2.SrNo).ToArray())
                })
                .ToArray();


            //Print It Out To Verify

            for (int i = 0; i < boxesOrganized.Length; i++)
            {
                Debug.WriteLine(""
                    + $"{boxesOrganized[i].ToString()}");

            }
        }

In my experience I try to avoid Linq, as it can get somewhat difficult to follow. So I'll leave you with an alternative to keep it simple and have the same result.

        public static void GroupWithDictionary() {


            List<Box> boxes =
                new List<Box>(){
                new Box(){ Id=1, Name="A1", SrNo="A11"},
                new Box(){ Id=2, Name="A1", SrNo="A22"},
                new Box(){ Id=3, Name="B1", SrNo="B11"},
                new Box(){ Id=4, Name="B1", SrNo="B22"},
                new Box(){ Id=5, Name="C1", SrNo="C11"},
                new Box(){ Id=6, Name="D1", SrNo="D11"}
            };


            Dictionary<string, List<Box>> boxDictionary =
                new Dictionary<string, List<Box>>();

            Box[][] _boxes = 
                new Box[][] { };    

            foreach (Box _box in boxes)
            {
                if (!boxDictionary.ContainsKey(_box.Name))   //If your box name doesn't exit
                {                                           //Add It To The Dictionary
                    boxDictionary.Add(                      
                        key: _box.Name,
                        value: new List<Box>());            //Initilize A New List For the Box Name

                }   

                boxDictionary[_box.Name].Add(           //Now Add The Box To The List Of Boxes
                    item: _box);                        //With The Given Name
            }


            _boxes =                                    //Dump Into An Array Of Box Arrays
                boxDictionary                           //Or Whatever Suits Your Needs              
                .Select(s => s.Value.ToArray())
                .ToArray();


                                                        //Print It Out To Verify

            for (int i = 0; i < _boxes.Length; i++)
            {
                Debug.WriteLine(""
                    + $"Id: {_boxes[i][0].Name}\t"
                    +$"Name: {_boxes[i][0].Name}\t"
                    +$"{string.Join(", ",_boxes[i].Select(s => s.SrNo))}");

            }

               


        }
Alex8695
  • 479
  • 2
  • 5
0

An interesting approach would be to use Aggregate. GroupBy is definitely natural here, and the answers using it are correct to do so, but it's useful to understand Aggregate because it enables some interesting scenarios for ad hoc transformations.

boxes.Aggregate(
    seed: new Dictionary<string, List<Box>>(),
    func: (results, box) => results.GetValueOrDefault(box.Name, new () { box }).Add(box),
    resultSelector: results => results.Select(e => new Box
    {
        Name = e.Key,
        Id = e.First().Id, 
        SrNo = string.Join(", ", e.Select(box => box.SrNo).Distinct())
    })
Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84