Получить отдельные строки из списка, затем создать новый объект и добавить его в тот же список

#c#

Вопрос:

Я пытаюсь найти различные имена a List<Person> на основе Name свойства. Для каждого отдельного имени я хочу создать новый объект с тем же свойством name, а все остальные свойства равны нулю. Как мне сделать это эффективно?

Класс Person .

 public class Person
{
    public string Name { get; set; }
    public string Country { get; set; }
}
 

Program.cs

 List<Person> personList = new List<Person>();

var p1 = new Person()
{
    Name = "John",
    Country = "USA"
};

var p2 = new Person()
{
    Name = "John",
    Country = "China"
};

var p3 = new Person()
{
    Name = "Bob",
    Country = "Italy"
};

var p4 = new Person()
{
    Name = "Bob",
    Country = "Brazil"
};

var p5 = new Person()
{
    Name = "Bob",
    Country = "Canada"
};

personList.Add(p1);
personList.Add(p2);
personList.Add(p3);
personList.Add(p4);
personList.Add(p5);
 

Цель состоит в том, чтобы добавить еще одного «Джона» и еще одного «Боба» в тот же список (потому что у них разные имена) со свойствами страны в качестве нулевой строки.

Комментарии:

1. personList.GroupBy(x => x.Name).Select(x => new Person{ Name = x.Key }).ToArray()

2. @MetroSmurf не работает!

3. Конечно, это так: dotnetfiddle.net/YY7kSS

4. Добавлен ответ с возможностью добавления различных значений в существующий список.

5. Пожалуйста, откройте новый вопрос, поскольку это совершенно другой вопрос. Однако сначала выполните поиск ответа SO, поскольку его задавали и отвечали несколько раз.

Ответ №1:

Использование запроса Linq упростит поиск всех отдельных Name значений:

 // first use .GroupBy to group everything by the Name property
// and then select a new Person for each grouping of Name

Person[] unique = personList.GroupBy(x => x.Name)
    .Select(x => new Person{Name = x.Key})
    .ToArray();
 

После создания новой коллекции; добавьте их в существующую коллекцию:

 personList.AddRange(unique);
 

Редактировать для использования с .Disinct

В качестве альтернативы, вы можете получить различные значения имен из метода Linq .Distinct() вместо использования .GroupBy() метода, но вам нужно выбрать .Name свойство, чтобы получить OOB distinct comparer:

 Person[] unique = personList.Select(x => x.Name)
    .Distinct()
    .Select(x => new Person{Name = x})
    .ToArray();

personList.AddRange(unique);
 

Использование Distinct требует либо использования встроенного IEqualityComparer , либо предоставления пользовательского IEqualityComparer в перегрузке. Простая передача самого объекта недопустима, если объект не реализует IEqualityComparer . В этом случае проще либо выбрать Name свойство и вызвать .Distinct , которое будет использовать встроенное IEqualityComparer , либо использовать .GroupBy метод, как в первом примере.

Ответ №2:

Используйте метод Distinct от linq (https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinct?view=net-5.0 ):

 public class Person
        {
            
            public string Name { get; set; }
            public string Country { get; set; }
    
        }
    
    // Program.cs
    
    List<Person> personList = new List<Person> ();
    
                var p1 = new Person ()
                {
                    Name = "John",
                    Country = "USA"
                };
    
    
                var p2 = new Person ()
                {
                    Name = "John",
                    Country = "China"
                };
    
                var p3 = new Person ()
                {
                    Name = "Bob",
                    Country = "Italy"
                };
    
                var p4 = new Person ()
                {
                    Name = "Bob",
                    Country = "Brazil"
                };
    
                var p5 = new Person ()
                {
                    Name = "Bob",
                    Country = "Canada"
                };
    
    
                personList.Add(p1);
                personList.Add (p2);
                personList.Add (p3);
                personList.Add (p4);
                personList.Add (p5);
    
       
    
        var personsWithDistinctNameInPersonList = personList.Select(p => p.Name).Distinct()
              .Select(n => new Person {
                Name = n,
              }).ToList();
           personList.AddRange(personsWithDistinctNameInPersonList);
 

Комментарии:

1. Это приводит к сбою как в методе Distinct (в качестве средства сравнения ничего не определено), так и затем завершится ошибкой с InvalidOperationException при попытке изменить коллекцию во время перечисления.

2. Поскольку я сначала выбираю только свойство name каждого пользователя, linq должен иметь возможность использовать встроенное по умолчанию средство сравнения строк просто отлично. В предоставленном фрагменте кода перечисление в списке пользователей не выполняется при вызове метода AddRange.

3. Я не видел .Select раньше. Итак, это работает, но InvalidOperationEx все равно будет возникать, потому что вам нужно материализовать запрос; как написано, он еще не материализован, пока вы не попытаетесь добавить его в ту же коллекцию.

4. personsWithDistinctNameInPersonList не вычисляется до .ToArray() тех пор, пока не будет вызвана операция, такая как .AddRange() или . Это называется отложенной оценкой. Как написано в настоящее время, он завершается с ошибкой. Точка. Вам нужно вызвать .ToArray() , чтобы ваш пример скомпилировался.

5. Я добавил ToList()