0

I have a class:

public class Item
{
    public string Name { get; set; }
    public bool IsFound { get; set; }
}

and 2 lists:

List<Item> first = new List<Item>();
List<Item> second = new List<Item>();
  • First list contains some predefined unique items
  • Second list contains also some unique items

How can I add the second list items to the first list without duplicates based on Name property, ignoring cases?

user2818430
  • 5,853
  • 21
  • 82
  • 148

8 Answers8

1
First.Union(Second).GroupBy(x=>x.Name).Select(x=>x.FirstOrDefault());
ryanyuyu
  • 6,366
  • 10
  • 48
  • 53
1

To add items from second into existing first list:

  List<Item> first = new List<Item>();
  List<Item> second = new List<Item>();
  ... 
  // ids to exclude while adding
  HashSet<String> ids = new HashSet<string>(first.Select(item => item.Name),
    StringComparer.InvariantCultureIgnoreCase);
  // add all items from the second, except exluded 
  first.AddRange(second.Where(item => !ids.Contains(item.Name)));
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
1

Here is a working solution with HashSet`1 and IEquatable`1. If you need to work with List`1s, you can use Linq as poined out in the answer by Ned Stoyanov.

Code:

class Program {
    static void Main(string[] args) {
        var first = new HashSet<Item>() {
            new Item() { Name = "a" },
            new Item() { Name = "b" },
        };
        var second = new HashSet<Item>() {
            new Item() { Name = "b" },
            new Item() { Name = "c" },
        };
        var both = first.Union(second).ToList();
        both.ForEach(Console.WriteLine);
        Console.ReadKey();
    }
}

public class Item : IEquatable<Item> {
    public string Name { get; set; }
    public bool IsFound { get; set; }

    public override bool Equals(object that) {
        return that is Item && this.Equals((Item)that);
    }

    public bool Equals(Item that) {
        return this.Name.Equals(that.Name, StringComparison.InvariantCultureIgnoreCase);
    }

    public override int GetHashCode() {
        return Name.GetHashCode();
    }

    public override string ToString() {
        return Name;
    }
}

Output:

a
b
c

Note: Due to the implementation of HashSet`1s, the order of elements is not respected.

Binkan Salaryman
  • 3,008
  • 1
  • 17
  • 29
0

If you know in advance that your lists have unique items, why are they not ISet< Item>? That's exactly what ISet's are for. Once you change them to ISets, you can simply call:

first.UnionWith(second);
shay__
  • 3,815
  • 17
  • 34
0

How can I add the second list items to the first list without duplicates based on Name property, ignoring cases?

This does what you want:

List<Item> third = first.Concat(second)
    .GroupBy(i => i.Name, StringComparer.InvariantCultureIgnoreCase)
    .Select(g => new Item
    {
        Name = g.Key,
        IsFound = g.Count() > 1
    })
    .ToList();

g.Count() > 1 is true only for items with the same name(ignoring case) that came from different lists, so g.Count() is either 1 or 2.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
0

If you implement a IEqualityComparer<Item> or better yet, use the KeyEqualityComparer from this answer. You can use Linq Union to get your result.

first.Union(second, new KeyEqualityComparer( i => i.Name));
Community
  • 1
  • 1
NeddySpaghetti
  • 13,187
  • 5
  • 32
  • 61
0

This should do it:

var distinct = from item in second
               where first.All(x => !String.Equals(x.Name, item.Name, StringComparison.OrdinalIgnoreCase))
               select item;
first.AddRange(distinct);
RePierre
  • 9,358
  • 2
  • 20
  • 37
0

You can have a combined list where only missing items from second are added.

   class Program
    {
        static void Main(string[] args)
        {
            var first = new List<Item> { new Item("one"), new Item("two") };
            var second = new List<Item> { new Item("two"), new Item("three") };
            var combined = new List<Item>();

            combined.AddRange(first);

            foreach (var item in second)
            {
                int index = combined.FindIndex(x => x.name == item.name);
                if (index <= 0)
                {
                    combined.Add(item);
                }

            }

            foreach (var item in combined)
            {
                Console.WriteLine(item.name);
            }

        }

        class Item
        {
            public string name;
            public Item(string name)
            {
                this.name = name;
            }
        }
    }
SpaceSteak
  • 224
  • 2
  • 15