У меня есть два набора datarows. Они каждый IEnumerable. Я хочу добавить/объединить эти два списка в один список. Я уверен, что это выполнимо. Я не хочу делать цикл for и замечаю, что есть метод Union и метод Join в двух списках. Любые идеи?
Добавление/конкатенация двух последовательностей IEnumerable
Ответ 1
Предполагая, что ваши объекты одного типа, вы можете использовать либо Union
, либо Concat
. Обратите внимание, что, как и ключевое слово SQL Union
, операция Union
гарантирует, что дубликаты будут устранены, тогда как Concat
(например, UNION ALL
) просто добавит второй список в конец первого.
IEnumerable<T> first = ...;
IEnumerable<T> second = ...;
IEnumerable<T> combined = first.Concat(second);
или
IEnumerable<T> combined = first.Union(second);
Если они имеют разные типы, тогда вам придется Select
их перевести в нечто общее. Например:
IEnumerable<TOne> first = ...;
IEnumerable<TTwo> second = ...;
IEnumerable<T> combined = first.Select(f => ConvertToT(f)).Concat(
second.Select(s => ConvertToT(s)));
Где ConvertToT(TOne f)
и ConvertToT(TTwo s)
представляют операцию, которая каким-то образом преобразует экземпляр TOne
(и TTwo
, соответственно) в экземпляр T
.
Ответ 2
Я столкнулся с аналогичной ситуацией, когда мне нужно объединить несколько последовательностей.
Естественно поиск существующих решений в Google/StackOverflow, однако не нашел ничего, что не оценило перечислимое, например. конвертировать в массив, затем использовать Array.Copy()
и т.д., поэтому я написал метод расширения и статического utiltiy под названием ConcatMultiple
.
Надеюсь, это поможет любому, кто должен сделать то же самое.
/// <summary>
/// Concatenates multiple sequences
/// </summary>
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
/// <param name="first">The first sequence to concatenate.</param>
/// <param name="source">The other sequences to concatenate.</param>
/// <returns></returns>
public static IEnumerable<TSource> ConcatMultiple<TSource>(this IEnumerable<TSource> first, params IEnumerable<TSource>[] source)
{
if (first == null)
throw new ArgumentNullException("first");
if (source.Any(x => (x == null)))
throw new ArgumentNullException("source");
return ConcatIterator<TSource>(source);
}
private static IEnumerable<TSource> ConcatIterator<TSource>(IEnumerable<TSource> first, params IEnumerable<TSource>[] source)
{
foreach (var iteratorVariable in first)
yield return iteratorVariable;
foreach (var enumerable in source)
{
foreach (var iteratorVariable in enumerable)
yield return iteratorVariable;
}
}
/// <summary>
/// Concatenates multiple sequences
/// </summary>
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
/// <param name="source">The sequences to concatenate.</param>
/// <returns></returns>
public static IEnumerable<TSource> ConcatMultiple<TSource>(params IEnumerable<TSource>[] source)
{
if (source.Any(x => (x == null)))
throw new ArgumentNullException("source");
return ConcatIterator<TSource>(source);
}
private static IEnumerable<TSource> ConcatIterator<TSource>(params IEnumerable<TSource>[] source)
{
foreach (var enumerable in source)
{
foreach (var iteratorVariable in enumerable)
yield return iteratorVariable;
}
}
Ответ 3
Метод Join похож на соединение SQL, где список перекрестно ссылается на основе условия, это не строка конкатенация или добавление в список. Метод Union делает то, что вы хотите, а также Concat, но оба являются оценками LAZY и имеют требование, чтобы параметры были не пустыми. Они возвращают либо ConcatIterator, либо UnionIterator, и если вызвано многократно, это может вызвать проблемы. Нежелательная оценка приводит к разному поведению, если это то, что вы хотите, тогда можно использовать метод расширения, подобный приведенному ниже.
public static IEnumerable<T> myEagerConcat<T>(this IEnumerable<T> first,
IEnumerable<T> second)
{
return (first ?? Enumerable.Empty<T>()).Concat(
(second ?? Enumerable.Empty<T>())).ToList();
}
Ответ 4
Задержка вызова второго и последующих перечислимых элементов
Я обычно использую Linq IEnumerable<T>.Concat()
, но сегодня мне нужно было на 100% быть уверенным, что второе перечисление не было перечислено, пока первое не было обработано до конца. (например, два запроса базы данных, которые я не хотел выполнять одновременно). Таким образом, следующая функция позволила задержать перечисления.
IEnumerable<T> DelayedConcat<T>(params Func<IEnumerable<T>>[] enumerableList)
{
foreach(var enumerable in enumerableList)
{
foreach (var item in enumerable())
{
yield return item;
}
}
}
Использование:
return DelayedConcat(
() => GetEnumerable1(),
() => GetEnumerable2(),
// and so on.. () => GetEnumerable3(),
);
В этом примере вызов функции GetEnumerable2 будет отложен до тех пор, пока GetEnumerable1 не будет перечислен до конца.