Miguel Gamboa
Miguel Gamboa

Reputation: 9343

How can I add an IEnumerable<T> to an existing ICollection<T>

Given an existing ICollection<T> instance (e.g. dest) what is the most efficient and readable way to add items from an IEnumerable<T>?

In my use case, I have some kind of utility method Collect(IEnumerable items) which returns a new ICollection with the elements from items, so I am doing it in the following way:

public static ICollection<T> Collect<T>(IEnumerable<T> items) where T:ICollection<T>
{
    ...
    ICollection<T> dest = Activator.CreateInstance<T>();
    items.Aggregate(dest, (acc, item) => { acc.Add(item); return acc; });
    ...
    return dest;
}

Question: Is there any “better” way (more efficient or readable) of doing it?

UPDATE: I think the use of Aggregate() is quite fluent and not so inefficient as invoking ToList().ForEach(). But it does not look very readable. Since nobody else agrees with the use of Aggregate() I would like to read your reasons to NOT use Aggregate() for this purpose.

Upvotes: 13

Views: 9205

Answers (5)

Gaspium
Gaspium

Reputation: 125

Most efficient:

foreach(T item in itens) dest.Add(item)

Most readable (BUT inefficient because it is creating a throwaway list):

items.ToList().ForEach(dest.Add);

Less readable, but Not so inefficient:

items.Aggregate(dest, (acc, item) => { acc.Add(item); return acc; });

Upvotes: 2

Matthew Watson
Matthew Watson

Reputation: 109537

We actually wrote an extension method for this (along with a bunch of other ICollection extension methods):

public static class CollectionExt
{
    public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> source)
    {
        Contract.Requires(collection != null);
        Contract.Requires(source != null);

        foreach (T item in source)
        {
            collection.Add(item);
        }
    }
}

So we can just use AddRange() on an ICollection():

ICollection<int> test = new List<int>();
test.AddRange(new [] {1, 2, 3});

Note: If you wanted to use List<T>.AddRange() if the underlying collection was of type List<T> you could implement the extension method like so:

public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> source)
{
    var asList = collection as List<T>;

    if (asList != null)
    {
        asList.AddRange(source);
    }
    else
    {
        foreach (T item in source)
        {
            collection.Add(item);
        }
    }
}

Upvotes: 4

Tim Schmelter
Tim Schmelter

Reputation: 460018

Just use Enumerable.Concat:

IEnumerable<YourType> result = dest.Concat(items);

If you want a List<T> as result use ToList:

List<YourType> result = dest.Concat(items).ToList();
// perhaps:
dest = result;

If dest is actually already a list and you want to modify it use AddRange:

dest.AddRange(items);

Update:

if you have to add items to a ICollection<T> method argument you could use this extension:

public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> seq)
{
    List<T> list = collection as List<T>;
    if (list != null)
        list.AddRange(seq);
    else
    {
        foreach (T item in seq)
            collection.Add(item);
    }
}

// ...

public static void Foo<T>(ICollection<T> dest)
{
    IEnumerable<T> items = ... 
    dest.AddRange(items);
}

Upvotes: 16

Sebastian Siemens
Sebastian Siemens

Reputation: 2421

items.ToList().ForEach(dest.Add);

If you dont want to create a new collection instance, then create an extension method.

public static class Extension
{
    public static void AddRange<T>(this ICollection<T> source, IEnumerable<T> items)
    {
        if (items == null)
        {
            return;
        }

        foreach (T item in items)
        {
            source.Add(item);
        }
    }
}

Then you can edit your code like this:

        ICollection<T> dest = ...;
        IEnumerable<T> items = ...;
        dest.AddRange(items);

Upvotes: 1

user743382
user743382

Reputation:

Personally I'd go with @ckruczek's comment of a foreach loop:

foreach (var item in items)
    dest.Add(item);

Simple, clean, and pretty much everybody immediately understands what it does.

If you do insist on some method call hiding the loop, then some people define a custom ForEach extension method for IEnumerable<T>, similar to what's defined for List<T>. The implementation is trivial:

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) {
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (action == null) throw new ArgumentNullException(nameof(action));
    foreach (item in source)
        action(item);
}

Given that, you would be able to write

items.ForEach(dest.Add);

I don't see much benefit in it myself, but no drawbacks either.

Upvotes: 10

Related Questions