Joining two lists together
Asked Answered
D

15

452

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.

Dichlorodiphenyltrichloroethane answered 6/10, 2009 at 21:22 Comment(2)
Does order matter? Do you want to retain duplicates?Perilune
Possible duplicate of How do you concatenate Lists in C#?.Burson
B
751

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.

Beaudette answered 6/10, 2009 at 21:24 Comment(6)
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.Reese
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)Externalize
I recommend you check this good comparison of he various approaches. Also show some performance analysis of various options: Merge CollectionsUmpire
This works perfectly. Was using concat() on more than half a million items, which took several minutes. This approach takes less than 5 seconds.Reisinger
hi.. is concat function return null if the list is empty?Racine
and second question.. is concat works for object? not only int or string..?Racine
I
141

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.

Insipience answered 6/10, 2009 at 21:25 Comment(1)
remember to using System.Linq; to be able to use ConcatX
P
49

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 } 
Perilune answered 6/10, 2009 at 21:24 Comment(0)
S
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
Sedgewick answered 6/10, 2009 at 21:24 Comment(1)
+1 for use of an EqualityComparer! I was able to use it for better handling of my lists of custom objects.Kaylee
C
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.

Coward answered 23/6, 2014 at 9:21 Comment(0)
R
14

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

var all = list1.Concat(list2).Concat(list3) ... Concat(listN).Distinct().ToList();
Radarman answered 19/7, 2013 at 11:13 Comment(0)
F
7

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

list2.AddRange(list1);
Felishafelita answered 6/10, 2009 at 21:26 Comment(0)
T
7
var bigList = new List<int> { 1, 2, 3 }
    .Concat(new List<int> { 4, 5, 6 })
    .ToList(); /// yields { 1, 2, 3, 4, 5, 6 }
Todd answered 6/10, 2009 at 21:26 Comment(4)
I like this too. Very simple. Thanks.Dichlorodiphenyltrichloroethane
I like this because it doesn't mutate either list.Toms
Is this like Union All? Or not? What if number 1 exist on all of lists?Racine
Concat is more/less "Union All"Todd
S
6

The AddRange method

aList.AddRange( anotherList );
Surtax answered 6/10, 2009 at 21:25 Comment(0)
V
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);
Vespiary answered 27/1, 2014 at 5:41 Comment(0)
P
3

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

Postpaid answered 6/10, 2009 at 21:25 Comment(0)
L
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()));
Levee answered 6/6, 2018 at 15:43 Comment(0)
C
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
*/
Chantress answered 19/1, 2016 at 18:1 Comment(0)
C
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.

Crepuscular answered 11/4, 2018 at 14:52 Comment(0)
D
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.

Damnatory answered 27/4, 2017 at 8:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.