使用扩展方法,我们可以编写方便的LINQ操作符来解决通用问题。
我想听听您在System.Linq
命名空间中缺少哪些方法或重载以及您如何实现它们。
干净而优雅的实现,最好使用现有的方法。
使用扩展方法,我们可以编写方便的LINQ操作符来解决通用问题。
我想听听您在System.Linq
命名空间中缺少哪些方法或重载以及您如何实现它们。
干净而优雅的实现,最好使用现有的方法。
这是我版本的Zip
,它像真正的拉链一样工作。它不会将两个值合并为一个,而是返回一个组合的IEnumerable
。可以进行重载,跳过右侧和/或左侧的尾部。
public static IEnumerable<TSource> ZipMerge<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second)
{
using (var secondEnumerator = second.GetEnumerator())
{
foreach (var item in first)
{
yield return item;
if (secondEnumerator.MoveNext())
yield return secondEnumerator.Current;
}
while (secondEnumerator.MoveNext())
yield return secondEnumerator.Current;
}
}
Zip
以外的其他名称?(我知道参数足以区分,但为了代码的可读性...) - Timwisecond
的第一个元素?还是我对代码理解有误? - AistinaMoveNext
存在误解。第一次调用告诉你枚举器是否为空。Current
然后包含第一个元素。 - Nappy这是一个简单的函数,如果您有一个中等到大型的数据集(比如100个以上的项),并且想要随机抽取一部分进行观察,那么这个函数非常有用。
public static IEnumerable<T> RandomSample<T>(this IEnumerable<T> source,
double percentage)
{
source.ThrowIfNull("source");
var r = new Random();
return source.Where(x => (r.NextDouble() * 100.0) < percentage);
}
使用方法:
List<DataPoint> data = GetData();
// Sample roughly 3% of the data
var sample = data.RandomSample(3.0);
// Verify results were correct for this sample
foreach (DataPoint point in sample)
{
Console.WriteLine("{0} => {1}", point, DoCalculation(point));
}
注意事项:
source.OrderBy(r.NextDouble()).Take(x);
- mattmc3枚举长度为size
的数组(“窗口”),其中包含最新的值。
{ 0, 1, 2, 3 }
变成了 { [0, 1], [1, 2], [2, 3] }
。
例如,我使用此方法连接两个点以绘制线图。
public static IEnumerable<TSource[]> Window<TSource>(
this IEnumerable<TSource> source)
{
return source.Window(2);
}
public static IEnumerable<TSource[]> Window<TSource>(
this IEnumerable<TSource> source, int size)
{
if (size <= 0)
throw new ArgumentOutOfRangeException("size");
return source.Skip(size).WindowHelper(size, source.Take(size));
}
private static IEnumerable<TSource[]> WindowHelper<TSource>(
this IEnumerable<TSource> source, int size, IEnumerable<TSource> init)
{
Queue<TSource> q = new Queue<TSource>(init);
yield return q.ToArray();
foreach (var value in source)
{
q.Dequeue();
q.Enqueue(value);
yield return q.ToArray();
}
}
public static bool One<T>(this IEnumerable<T> enumerable)
{
using (var enumerator = enumerable.GetEnumerator())
return enumerator.MoveNext() && !enumerator.MoveNext();
}
public static bool Two<T>(this IEnumerable<T> enumerable)
{
using (var enumerator = enumerable.GetEnumerator())
return enumerator.MoveNext() && enumerator.MoveNext() && !enumerator.MoveNext();
}
public static bool MoreThanOne<T>(this IEnumerable<T> enumerable)
{
return enumerable.Skip(1).Any();
}
public static bool AtLeast<T>(this IEnumerable<T> enumerable, int count)
{
using (var enumerator = enumerable.GetEnumerator())
for (var i = 0; i < count; i++)
if (!enumerator.MoveNext())
return false;
return true;
}
public static bool AnyAtAll<T>(this IEnumerable<T> enumerable)
{
return enumerable != null && enumerable.Any();
}
ToEnumerable
,因为它似乎与其它内容无关,并且已经在另一个答案中发布过了。 - Timwi/// <summary>
/// Enumerates the items of this collection, skipping the last
/// <paramref name="count"/> items. Note that the memory usage of this method
/// is proportional to <paramref name="count"/>, but the source collection is
/// only enumerated once, and in a lazy fashion. Also, enumerating the first
/// item will take longer than enumerating subsequent items.
/// </summary>
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
if (source == null)
throw new ArgumentNullException("source");
if (count < 0)
throw new ArgumentOutOfRangeException("count",
"count cannot be negative.");
if (count == 0)
return source;
return skipLastIterator(source, count);
}
private static IEnumerable<T> skipLastIterator<T>(IEnumerable<T> source,
int count)
{
var queue = new T[count];
int headtail = 0; // tail while we're still collecting, both head & tail
// afterwards because the queue becomes completely full
int collected = 0;
foreach (var item in source)
{
if (collected < count)
{
queue[headtail] = item;
headtail++;
collected++;
}
else
{
if (headtail == count) headtail = 0;
yield return queue[headtail];
queue[headtail] = item;
headtail++;
}
}
}
/// <summary>
/// Returns a collection containing only the last <paramref name="count"/>
/// items of the input collection. This method enumerates the entire
/// collection to the end once before returning. Note also that the memory
/// usage of this method is proportional to <paramref name="count"/>.
/// </summary>
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
if (source == null)
throw new ArgumentNullException("source");
if (count < 0)
throw new ArgumentOutOfRangeException("count",
"count cannot be negative.");
if (count == 0)
return new T[0];
var queue = new Queue<T>(count + 1);
foreach (var item in source)
{
if (queue.Count == count)
queue.Dequeue();
queue.Enqueue(item);
}
return queue.AsEnumerable();
}
TakeLast
可以用于 SkipLast
,只需使用 yield return queue.Dequeue();
。 - Mark HurdWhereIf
是一个可选的IEnumerable
和IQueryable
中的筛选条件。它能够避免在构建查询谓词和lambda表达式时使用if语句。当你不知道在编译时是否应该应用过滤器时,它非常有用。
public static IEnumerable<TSource> WhereIf<TSource>(
this IEnumerable<TSource> source, bool condition,
Func<TSource, bool> predicate)
{
return condition ? source.Where(predicate) : source;
}
用法:
var custs = Customers.WhereIf(someBool, x=>x.EyeColor=="Green");
mySearchResults.WhereIf(chkShowOnlyUnapproved.Checked, x=>!x.IsApproved)
。 - KeithS与 Ani 的 AssertCount
方法(我使用一个称为 CountAtLeast
的方法)一起使用,可以很容易地找到序列中出现多次的元素:
public static IEnumerable<T> Duplicates<T, TKey>(this IEnumerable<T> source,
Func<T, TKey> keySelector = null, IEqualityComparer<TKey> comparer = null)
{
source.ThrowIfNull("source");
keySelector = keySelector ?? new Func<T, TKey>(x => x);
comparer = comparer ?? EqualityComparer<TKey>.Default;
return source.GroupBy(keySelector, comparer)
.Where(g => g.CountAtLeast(2))
.SelectMany(g => g);
}
g.CountAtLeast(2)
写成g.Skip(1).Any()
。 - TimwiSkipNulls()
,它只是 Where(x => x != null)
)。我使用它们并不是因为它们做了很多额外的工作,而是因为我发现它们使代码更易读(在适当的情况下将几个方法调用包装成一个以便于代码重用也不错)。 - Dan TaoSkipNulls<T>()
实际上就是 OfType<T>()
。 - GabeOfType<T>
或 Where<T>
,两种方式都只是一个微不足道的包装器。我的意思只是 SkipNulls
这个名称让它更有目的性。 - Dan TaoToList和ToDictionary可以暴露基础集合类的初始容量重载。当源长度已知或有限时,偶尔会有用。
public static List<TSource> ToList<TSource>(
this IEnumerable<TSource> source,
int capacity)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
var list = new List<TSource>(capacity);
list.AddRange(source);
return list;
}
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
int capacity,
IEqualityComparer<TKey> comparer = null)
{
return source.ToDictionary<TSource, TKey, TSource>(
keySelector, x => x, capacity, comparer);
}
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
int capacity,
IEqualityComparer<TKey> comparer = null)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (keySelector == null)
{
throw new ArgumentNullException("keySelector");
}
if (elementSelector == null)
{
throw new ArgumentNullException("elementSelector");
}
var dictionary = new Dictionary<TKey, TElement>(capacity, comparer);
foreach (TSource local in source)
{
dictionary.Add(keySelector(local), elementSelector(local));
}
return dictionary;
}
static int CountUpTo<T>(this IEnumerable<T> source, int maxCount)
{
if (maxCount == 0)
return 0;
var genericCollection = source as ICollection<T>;
if (genericCollection != null)
return Math.Min(maxCount, genericCollection.Count);
var collection = source as ICollection;
if (collection != null)
return Math.Min(maxCount, collection.Count);
int count = 0;
foreach (T item in source)
if (++count >= maxCount)
break;
return count;
}
collection.Take(maxCount).Count()
是一样的,对吗? - Timwipublic static T Coalesce<T>(this IEnumerable<T> items) {
return items.Where(x => x != null && !x.Equals(default(T))).FirstOrDefault();
// return items.OfType<T>().FirstOrDefault(); // Gabe's take
}
Nothing
不等同于null
... 必须修改C#实现。 - mattmc3