MoreLinq提供了FullJoin扩展。但是,我希望 bothSelector 函数(它是 FullJoin 函数的参数(仅在它不为空时才返回 TResult。
例:
给定两个由数字完全连接的列表:
列表 1: 1,2,3 列表 2: 1,2,3,4,5
结果列表 3 : 空, 空, 空, 4, 5
期望: 4, 5
public void X()
{
var list1 = new List<int> { 1, 2, 3, 4, 5 };
var list2 = new List<int> { 4, 5 };
list1.FullJoin(
list2,
item => item,
item1 => item1,
item2 => item2,
(item1, item2) => item1);
}
这可能吗?
谢谢。
使用原始的 FullJoin 代码稍作修改:
/// <summary>
/// Full join without returning null values.
/// MoreLinq - https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/FullJoin.cs
/// </summary>
public static IEnumerable<TResult> FullJoinExceptNull<TFirst, TSecond, TKey, TResult>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TKey> firstKeySelector,
Func<TSecond, TKey> secondKeySelector,
Func<TFirst, TResult> firstSelector,
Func<TSecond, TResult> secondSelector,
Func<TFirst, TSecond, TResult> bothSelector,
IEqualityComparer<TKey> comparer = null)
{
if (first == null) throw new ArgumentNullException(nameof(first));
if (second == null) throw new ArgumentNullException(nameof(second));
if (firstKeySelector == null) throw new ArgumentNullException(nameof(firstKeySelector));
if (secondKeySelector == null) throw new ArgumentNullException(nameof(secondKeySelector));
if (firstSelector == null) throw new ArgumentNullException(nameof(firstSelector));
if (secondSelector == null) throw new ArgumentNullException(nameof(secondSelector));
if (bothSelector == null) throw new ArgumentNullException(nameof(bothSelector));
return _(); IEnumerable<TResult> _()
{
var seconds = second.Select(e => new KeyValuePair<TKey, TSecond>(secondKeySelector(e), e)).ToArray();
var secondLookup = seconds.ToLookup(e => e.Key, e => e.Value, comparer);
var firstKeys = new HashSet<TKey>(comparer);
foreach (var fe in first)
{
var key = firstKeySelector(fe);
firstKeys.Add(key);
using var se = secondLookup[key].GetEnumerator();
if (se.MoveNext())
{
do
{
var result = bothSelector(fe, se.Current);
if (result == null) continue;
yield return result;
}
while (se.MoveNext());
}
else
{
se.Dispose();
yield return firstSelector(fe);
}
}
foreach (var se in seconds)
{
if (!firstKeys.Contains(se.Key))
yield return secondSelector(se.Value);
}
}
}