436

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
GurdeepS
  • 65,107
  • 109
  • 251
  • 387

15 Answers15

726

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.

John Smith
  • 7,243
  • 6
  • 49
  • 61
ChrisF
  • 134,786
  • 31
  • 255
  • 325
  • 49
    No one's really gone into when to use which method. AddRange edits a list in place, adding the second list to it (as if you called .Add(foo) a bunch of times). The Concat and Union extension methods don't change the original list. They lazily construct a new IEnumerable and won't even access the original list members unless necessary. As noted, Union removes duplicates while the others don't. – ShawnFumo Jan 02 '15 at 02:15
  • 4
    Does anyone know what is the complexity of this method? (It is so shame Microsoft doesn't supply this important information as part of their MSDN) – Jacob Nov 14 '16 at 18:00
  • 2
    I recommend you check this good comparison of he various approaches. Also show some performance analysis of various options: [Merge Collections](https://alicebobandmallory.com/articles/2012/10/18/merge-collections-without-duplicates-in-c) – BogeyMan Jan 15 '19 at 23:25
  • 1
    This works perfectly. Was using concat() on more than half a million items, which took several minutes. This approach takes less than 5 seconds. – GreenFerret95 Nov 07 '19 at 18:50
  • hi.. is concat function return null if the list is empty? – toha Jan 16 '22 at 09:36
  • and second question.. is concat works for object? not only int or string..? – toha Jan 16 '22 at 09:37
138

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.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
48

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 } 
Larsenal
  • 49,878
  • 43
  • 152
  • 220
27

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
Vivek Jain
  • 3,811
  • 6
  • 30
  • 47
Frederik Gheysels
  • 56,135
  • 11
  • 101
  • 154
  • 1
    +1 for use of an EqualityComparer! I was able to use it for better handling of my lists of custom objects. – CANDIMAN Nov 13 '20 at 01:03
21
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Balasubramani M
  • 7,742
  • 2
  • 45
  • 47
14

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

var all = list1.Concat(list2).Concat(list3) ... Concat(listN).Distinct().ToList();
jimpanzer
  • 3,470
  • 4
  • 44
  • 84
7

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

list2.AddRange(list1);
Badaro
  • 3,460
  • 1
  • 19
  • 18
7
var bigList = new List<int> { 1, 2, 3 }
    .Concat(new List<int> { 4, 5, 6 })
    .ToList(); /// yields { 1, 2, 3, 4, 5, 6 }
hichris123
  • 10,145
  • 15
  • 56
  • 70
David
  • 19,389
  • 12
  • 63
  • 87
6

The AddRange method

aList.AddRange( anotherList );
Greg
  • 16,540
  • 9
  • 51
  • 97
4
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);
Vivek Jain
  • 3,811
  • 6
  • 30
  • 47
Krishan
  • 2,356
  • 6
  • 36
  • 47
3

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

Prasad Telkikar
  • 15,207
  • 5
  • 21
  • 44
Mark Redman
  • 24,079
  • 20
  • 92
  • 147
2

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()));
Drew Chapin
  • 7,779
  • 5
  • 58
  • 84
1

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
*/
Miroslav Siska
  • 401
  • 4
  • 15
1

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.

Georg
  • 11
  • 2
0

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.

quicktrick
  • 112
  • 1
  • 6