2

I have two Lists:

   List<Item1> list1;
   List<Item2> list2;

I have following code for compearing items in Lists:

ConcurrentDictionary<string, string> compareDictionary = new ConcurrentDictionary<string, string>();

for (int i = 0; i < lis1.Count; i++)
{
   var item1Name= list1[i].Name.ToString();
   var item1Id= list1[i].ID.ToString();

   foreach (var item in list2)
   {
      if (item1Name.Contains(item.item2Name.ToLower()))
      {
          compareDictionary.TryAdd(item1Id, item.item2Id);
      }
    }
}

It is needed to add Id from the first List an Id from the second one into ConcurrentDictionary, if Name of one item contains part of another one. It works, but I want to simplify the algorithm and remove the foreach and if.

  • 1
    Can you please share properties of `Item1` and `Item1`? – MKR Feb 24 '20 at 17:30
  • Join the two lists together with AddRange, eg. list1.AddRange(list2); Then list1.Distinct(). If you wanted to match on only some of the properties, then you would need to create a custom equality comparer, – level_zebra Feb 24 '20 at 17:39
  • 1
    @level_zebra the lists are of different types, you'll get a compilation error trying to AddRange list2 to list1. – Claudio Valerio Feb 24 '20 at 17:44

2 Answers2

1

You can get rid of the inner loop using System.Linq. Also simplify code by using foreach instead of for for main loop:

ConcurrentDictionary<string, string> compareDictionary = new ConcurrentDictionary<string, string>();

foreach (var element in list1)
{
   var item1Name= element.Name.ToString();
   var item1Id= element.ID.ToString();
   // If this element has already one correspondence, TryAdd will fail anyway
   if(compareDictionary.ContainsKey(item1Id)) continue; 

   var found = list2.FirstOrDefault(item => item1Name.Contains(item.item2Name.ToLower()));
   if(found != null)
   {
          compareDictionary.TryAdd(item1Id, item.item2Id);
   }
}

Depending on your elements naming, you also could need to make item1Name lower case. Your code will in fact find this correspondence:

Parentwithlittlechild --> LittleChild

but not this:

ParentWithLittleChild --> LittleChild

because "LittleChild" would be lower cased, and String.Contains works case sensitively by default. You could also use IndexOf method with StringComparison.OrdinalIgnoreCase, like this:

var found = list2.FirstOrDefault(item => item1Name.IndexOf(item.item2Name, StringComparison.OrdinalIgnoreCase) >= 0);

Final consideration: element.Name is probably already of type string, unless your Item1.Name property is some funny type. If so, element.Name.ToString() is convertible in just element.Name.

Claudio Valerio
  • 2,302
  • 14
  • 24
1

Couple of code snippets to find the matches and add the ids of the two objects in a ConcurrentDictionary<string, string>.

So we have:

var dict = new ConcurrentDictionary<string, string>();

First

list1.ForEach(x =>
{
    list2.Where(y => y.Item2Name.IndexOf(x.Name, StringComparison.CurrentCultureIgnoreCase) >= 0)
    .ToList().ForEach(y => dict.TryAdd(x.ID, y.Item2Id));
});

Second

(from x in list1
    from y in list2
    where x.Name.IndexOf(y.Item2Name, StringComparison.CurrentCultureIgnoreCase) >= 0
    select (x, y)).ToList().ForEach(item => dict.TryAdd(item.x.ID, item.y.Item2Id));

Side Note

  • If both Item1.Name and Item2.Item2Name properties are of string type, then no need to use ToString() function like in var item1Name= list1[i].Name.ToString(); line.

  • If both Item1.ID and Item2.Item2Id properties are of int type, then the proper type of both Key and Value pairs of the dictionary is the int type. So:

var dict = new ConcurrentDictionary<int, int>();