Reputation: 9343
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
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
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
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
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
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