合并List <T>中的重复项

问题描述

我有一个列表,如下所示。

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

我想通过按名称合并重复项来清理列表,以便输出看起来像这样。

var Boxes = {
        new Box(){ Id=1,SrNo="A11,A22"},SrNo="B11,B22"},SrNo="D11"}
        }

我了解我需要做GroupBy,但是如何更新SrNo,以便它从重复项中追加值并从列表中删除重复项行?

解决方法

类似

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();

这未经测试,我在手机上写下了。我认为它将满足您的要求。

,

对于以下结果

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

尝试以下代码:

class Program
{
    static void Main(string[] args)
    {
        var boxes = new List<Box>(){
            new Box(){ Id=1,Name="A1",SrNo="A11"},new Box(){ Id=2,SrNo="A22"},new Box(){ Id=3,Name="B1",SrNo="B11"},new Box(){ Id=4,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;
    }
}
,

让我们假设您的Box类是这样的。

        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}";
            }

        }

如果要使用linq,可以这样做:

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();

这将为您提供您想要的输出:

enter image description here

完整代码:

        public static void GroupMyBoxes()
        {
            List<Box> boxes =
                new List<Box>(){
                new Box(){ Id=1,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()}");

            }
        }

以我的经验,我尽量避免使用Linq,因为它可能会变得有些难以理解。因此,我将为您提供其他选择,以使其简单并获得相同的结果。

        public static void GroupWithDictionary() {


            List<Box> boxes =
                new List<Box>(){
                new Box(){ Id=1,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))}");

            }

               


        }
,

一种有趣的方法是使用AggregateGroupBy在这里绝对是自然的,使用它的答案是正确的,但是理解Aggregate很有用,因为它为临时转换提供了一些有趣的场景。

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,e.Select(box => box.SrNo).Distinct())
    })