Как я могу создать новый список на основе двух других списков и учесть дубликаты?

#c# #algorithm #sorting

Вопрос:

Мой первый пост. Униженный этим сообществом. Спасибо.

Цель: Создать новый List<PropertyB> на основе двух других списков: List<PropertyA> и еще List<PropertyB> одного .

Для каждого свойства в List<PropertyA> , создайте новое PropertyB() , присвоив отображаемое имя новому свойству Propertyb’s Name. Для каждого свойства в «Списке», если имя из PropertyA совпадает с PropertyB, присвоите значение свойству value нового списка.

Проблема: Учет повторяющихся значений. Между списками не может произойти потери данных.

Новый список должен включать: Каждое свойство и каждое значение списка свойств, в котором есть совпадение имен. Типы:

Мои мысли: Моя интуиция подсказывает, что внутренний цикл должен проверить, было ли что-то уже добавлено в коллекцию. Или, возможно, учет повторяющихся значений (т. е. индекс дубликатов?)

Любая помощь будет признательна!

  public class PropertyA{
      private string DisplayName{get; set;}
      private string Name {get; set;}
      private string Value {get; set;}
}
 
  public class PropertyB{
      private string Name{get; set;}
      private string Value{get; set;}
}
 

Инициализация:

 List<PropertyA> listA = new List<PropertyA>()
{
      new PropertyA(){ DisplayName="LOB", Name="lineofbusiness", Value="test"},
      new PropertyA(){ DisplayName="ABC", Name="alpha", Value="test2"},
      new PropertyA(){ DisplayName="DEF", Name="beta", Value="test3"},
      new PropertyA(){ DisplayName="GHI", Name="zeta", Value="test4"},
      new PropertyA(){ DisplayName"Line of Business", Name="lineofbusiness", Value="test5"
};
List<PropertyB> listB = new List<PropertyB>()
{
      new PropertyB(){ Name="lineofbusiness", Value="test789"},
      new PropertyB(){ Name="alpha", Value="test234"},
      new PropertyB(){ Name="lineofbusiness", Value="test456"},
      new PropertyB(){ Name="beta", Value="test123"},
};
 

В Основном:

 List<PropertyB> newList = new List<PropertyB>();

foreach(PropertyA propA in listA){
      PropertyB newProp = new PropertyB();
      newProp.Name = propA.DisplayName;
     
     foreach(PropertyB propB in listB){
     
          if(propA.Name == propB.Name){
               newProp.Value = propB.Value;
               break; 
          }
     }
     newList.Add(newProp);
}
 

ОБНОВЛЕНИЕ:
Вывод консоли (если вы выберете) должен быть следующим:

 LOB test789
ABC test234 
DEF test123 
GHI null 
Line of Business test456 
 

если вы просто удалите break; , вы в конечном итоге получите:

 LOB test456
ABC test234 
DEF test123 
GHI null 
Line of Business test456 
 

Внутренний цикл всегда будет присваивать значение соответствия ФАМИЛИИ. В этом-то и проблема.

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

1. Можете ли вы показать нам ожидаемый результат?

2. Что не так с вашим кодом?

3. Свойство B имеет меньше свойств, чем свойство А. Поэтому я не думаю, что есть потеря данных.

4. @wseipel Просто удалите перерыв. В чем проблема с этим? Я не понимаю, что делать со вторым значением? Ты собираешься сделать массив или как? Может быть, строка, разделенная запятыми, как это предлагается в ответе? Вот почему ваш вопрос неясен.

5. @Serge Позвольте мне уточнить: новый список должен содержать КАЖДОЕ отображаемое имя из списка<Свойство> (сохраненное в «Имя») и каждое значение из списка<Свойство><Свойство B>, где свойства имени совпадают. Удаление «break;» не приведет к получению первого ЗНАЧЕНИЯ, в котором имя=lineofbusiness. Удаление разрыва гарантирует, что он всегда сохранит ВТОРОЙ экземпляр, в котором имя = lineofbusiness.

Ответ №1:

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

 List<PropertyB> newList = new List<PropertyB>();

foreach(PropertyA propA in listA)
{

 PropertyB newProp = new PropertyB();
  newProp.Name = propA.DisplayName;

foreach (var propB in listB)
{
    if (propA.Name == propB.Name)
    {
        if( newList.Any(l =>l.Value==propB.Value )) continue;
        newProp.Value = propB.Value;
        break;
    }
}
 newList.Add(newProp);
}
 

но чтобы сделать его более надежным, я бы предложил это

     List<PropertyA> newList = new List<PropertyA>();

    foreach (var propA in listA)
    {
        var newProp = new PropertyA();
        newProp.Name = propA.DisplayName;
        
        newProp.DisplayName = propA.Name;
        
        foreach (var propB in listB)
        {
            if (propA.Name == propB.Name)
            {
            if (newList.Any(l => l.Value == propB.Value 
                           amp;amp; l.DisplayName==propA.Name)) continue;
                newProp.Value = propB.Value;
                break;
            }
        }
    
        newList.Add(newProp);
    }

    var result = newList.Select(l => new PropertyB {Name=l.Name, Value=l.Value} );
 

оба алгоритма показывают один и тот же результат во время теста

 LOB test789
ABC test234 
DEF test123 
GHI null 
Line of Business test456 
 

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

1. Объясните, что вы «исправили», чтобы ОП мог чему-то научиться.

2. Удаление «разрыва» не исправит этого. Совсем наоборот. И свойство не может быть изменено на массив.

3. @wseipel Хорошо, я изменил свой код. Теперь результат такой же, как вы хотите

4. Выдающийся! Интересно, как вы поменяли PropertyB на PropertyA в первом цикле. Спасибо!

Ответ №2:

Я понимал этот процесс:

  1. список потребностей A превращается в список потребностей B
  2. Некоторые элементы списка B могут иметь значение, скопированное из какого-либо другого списка B
     var d = bList.ToDictionary(b => b.Name, b => b.Value);
    var newB = aList.Select(a => new B { Name = a.DisplayName, Value = d.GetValueOrDefault(a.Name) } ).ToList();
 

Вы сказали, что никакие данные не будут потеряны, но я думаю, что по своей сути вы должны что-то выбросить, потому что B имеет меньше свойств, чем A, и некоторые свойства из B используются для «перезаписи»/замещения тех, что в A..

Я также отмечаю, что у вас есть дублированное имя в вашем образце списка данных B, которое ToDictionary не потерпит. Вы не указали, как решить эту проблему, но вам придется выбрать (если это действительно произойдет), какое значение выбрать или если принимать несколько. Это, например, допускает дублирование имен

     var d = bList.ToLookup(b => b.Name, b => b.Value);
    var newB = aList.Select(a => new B { Name = a.DisplayName, Value = d[a.Name]?.First() } ).ToList();
 

Опять же, это выбрасывает вещи.. если вы хотите сохранить все значения, вам придется каким-то образом закодировать значение

 Value = string.Join(",", d[a.Name])
 

например


Итак, похоже, вы хотите сохранить все дубликаты и распределить их по порядку. Мы могли бы сделать это, сгруппировав эти вещи в список, из которого мы извлекаем элементы по мере перечисления

     var d = bList.GroupBy(b => b.Name, b => b.Value).ToDictionary(g => g.Key, g => g.ToList());
   
    var newB = new List<B>();
    foreach(var a in aList){
      var b = new B { Name = a.DisplayName };

      if(d.TryGetValue(a.Name, out var lst)){
        b.Value = lst[0];
        lst.RemoveAt(0);
      }
    }
 

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

1. Вывод должен быть следующим: ` LOB test789 ABC test234 DEF test123 GHI нулевая бизнес-линия test123

2. Хотел уточнить: как минимум, в новом списке<PropertyB> должно быть каждое имя = List<PropertyB><PropertyA>.DisplayName. Итак, если в списке<PropertyA> 20 элементов, в новом списке<PropertyA><PropertyB>должно быть 20 элементов. Если имя не совпадает, значение нового свойства должно быть равно нулю.

3. Проблема заключается в дублировании «бизнес-направления». Как создать новое свойство С помощью этого value…as так же, как и в первом случае.

4. И это та часть, которую вам нужно прояснить. Какое направление бизнеса вы хотите? И если вы хотите и то, и другое (или некоторые, или все, что есть 3 , тогда какие и..), как вы их хотите?

5. Я опубликовал обновление того, как будет выглядеть образец вывода. Мне нужно и то, и другое: в новом списке должно быть 5 объектов недвижимости. Один из них будет иметь имя = LOB и значение test789, а другой должен иметь имя = Направление деятельности и значение test456.