385
votes

If I have two lists of type string (or any other type), what is a quick way of joining the two lists?

The order should stay the same. Duplicates should be removed (though every item in both links are unique). I didn't find much on this when googling and didn't want to implement any .NET interfaces for speed of delivery.

15
Does order matter? Do you want to retain duplicates? - Larsenal

15 Answers

657
votes

You could try:

List<string> a = new List<string>();
List<string> b = new List<string>();

a.AddRange(b);

MSDN page for AddRange

This preserves the order of the lists, but it doesn't remove any duplicates which Union would do.

This does change list a. If you wanted to preserve the original lists then you should use Concat (as pointed out in the other answers):

var newList = a.Concat(b);

This returns an IEnumerable as long as a is not null.

123
votes

The way with the least space overhead is to use the Concat extension method.

var combined = list1.Concat(list2);

It creates an instance of IEnumerable<T> which will enumerate the elements of list1 and list2 in that order.

45
votes

The Union method might address your needs. You didn't specify whether order or duplicates was important.

Take two IEnumerables and perform a union as seen here:

int[] ints1 = { 5, 3, 9, 7, 5, 9, 3, 7 };
int[] ints2 = { 8, 3, 6, 4, 4, 9, 1, 0 };

IEnumerable<int> union = ints1.Union(ints2);

// yields { 5, 3, 9, 7, 8, 6, 4, 1, 0 } 
27
votes

Something like this:

firstList.AddRange (secondList);

Or, you can use the 'Union' extension method that is defined in System.Linq. With 'Union', you can also specify a comparer, which can be used to specify whether an item should be unioned or not.

Like this:

List<int> one = new List<int> { 1, 2, 3, 4, 5 };
List<int> second=new List<int> { 1, 2, 5, 6 };

var result = one.Union (second, new EqComparer ());

foreach( int x in result )
{
    Console.WriteLine (x);
}
Console.ReadLine ();

#region IEqualityComparer<int> Members
public class EqComparer : IEqualityComparer<int>
{
    public bool Equals( int x, int y )
    {
        return x == y;
    }

    public int GetHashCode( int obj )
    {
        return obj.GetHashCode ();
    }
}
#endregion
19
votes
targetList = list1.Concat(list2).ToList();

It's working fine I think so. As previously said, Concat returns a new sequence and while converting the result to List, it does the job perfectly. Implicit conversions may fail sometimes when using the AddRange method.

14
votes

If some item(s) exist in both lists you may use

var all = list1.Concat(list2).Concat(list3) ... Concat(listN).Distinct().ToList();
8
votes

As long as they are of the same type, it's very simple with AddRange:

list2.AddRange(list1);
7
votes

The AddRange method

aList.AddRange( anotherList );
7
votes
var bigList = new List<int> { 1, 2, 3 }
    .Concat(new List<int> { 4, 5, 6 })
    .ToList(); /// yields { 1, 2, 3, 4, 5, 6 }
4
votes
List<string> list1 = new List<string>();
list1.Add("dot");
list1.Add("net");

List<string> list2 = new List<string>();
list2.Add("pearls");
list2.Add("!");

var result = list1.Concat(list2);
3
votes

one way: List.AddRange() depending on the types?

2
votes

One way, I haven't seen mentioned that can be a bit more robust, particularly if you wanted to alter each element in some way (e.g. you wanted to .Trim() all of the elements.

List<string> a = new List<string>();
List<string> b = new List<string>();
// ...
b.ForEach(x=>a.Add(x.Trim()));
1
votes

See this link

public class ProductA
{ 
public string Name { get; set; }
public int Code { get; set; }
}

public class ProductComparer : IEqualityComparer<ProductA>
{

public bool Equals(ProductA x, ProductA y)
{
    //Check whether the objects are the same object. 
    if (Object.ReferenceEquals(x, y)) return true;

    //Check whether the products' properties are equal. 
    return x != null && y != null && x.Code.Equals(y.Code) && x.Name.Equals(y.Name);
    }

public int GetHashCode(ProductA obj)
{
    //Get hash code for the Name field if it is not null. 
    int hashProductName = obj.Name == null ? 0 : obj.Name.GetHashCode();

    //Get hash code for the Code field. 
    int hashProductCode = obj.Code.GetHashCode();

    //Calculate the hash code for the product. 
    return hashProductName ^ hashProductCode;
}
}


    ProductA[] store1 = { new ProductA { Name = "apple", Code = 9 }, 
                   new ProductA { Name = "orange", Code = 4 } };

    ProductA[] store2 = { new ProductA { Name = "apple", Code = 9 }, 
                   new ProductA { Name = "lemon", Code = 12 } };

//Get the products from the both arrays //excluding duplicates.

IEnumerable<ProductA> union =
  store1.Union(store2);

foreach (var product in union)
    Console.WriteLine(product.Name + " " + product.Code);

/*
    This code produces the following output:

    apple 9
    orange 4
    lemon 12
*/
1
votes

The two options I use are:

list1.AddRange(list2);

or

list1.Concat(list2);

However I noticed as I used that when using the AddRange method with a recursive function, that calls itself very often I got an SystemOutOfMemoryException because the maximum number of dimensions was reached.

(Message Google Translated)
The array dimensions exceeded the supported range.

Using Concat solved that issue.

0
votes

I just wanted to test how Union works with the default comparer on overlapping collections of reference type objects.

My object is:

class MyInt
{
    public int val;

    public override string ToString()
    {
        return val.ToString();
    }
}

My test code is:

MyInt[] myInts1 = new MyInt[10];
MyInt[] myInts2 = new MyInt[10];
int overlapFrom = 4;
Console.WriteLine("overlapFrom: {0}", overlapFrom);

Action<IEnumerable<MyInt>, string> printMyInts = (myInts, myIntsName) => Console.WriteLine("{2} ({0}): {1}", myInts.Count(), string.Join(" ", myInts), myIntsName);

for (int i = 0; i < myInts1.Length; i++)
    myInts1[i] = new MyInt { val = i };
printMyInts(myInts1, nameof(myInts1));

int j = 0;
for (; j + overlapFrom < myInts1.Length; j++)
    myInts2[j] = myInts1[j + overlapFrom];
for (; j < myInts2.Length; j++)
    myInts2[j] = new MyInt { val = j + overlapFrom };
printMyInts(myInts2, nameof(myInts2));

IEnumerable<MyInt> myUnion = myInts1.Union(myInts2);
printMyInts(myUnion, nameof(myUnion));

for (int i = 0; i < myInts2.Length; i++)
    myInts2[i].val += 10;
printMyInts(myInts2, nameof(myInts2));
printMyInts(myUnion, nameof(myUnion));

for (int i = 0; i < myInts1.Length; i++)
    myInts1[i].val = i;
printMyInts(myInts1, nameof(myInts1));
printMyInts(myUnion, nameof(myUnion));

The output is:

overlapFrom: 4
myInts1 (10): 0 1 2 3 4 5 6 7 8 9
myInts2 (10): 4 5 6 7 8 9 10 11 12 13
myUnion (14): 0 1 2 3 4 5 6 7 8 9 10 11 12 13
myInts2 (10): 14 15 16 17 18 19 20 21 22 23
myUnion (14): 0 1 2 3 14 15 16 17 18 19 20 21 22 23
myInts1 (10): 0 1 2 3 4 5 6 7 8 9
myUnion (14): 0 1 2 3 4 5 6 7 8 9 20 21 22 23

So, everything works fine.