使用扩展方法,我们可以编写方便的LINQ运算符来解决一般问题。
我想知道System.Linq
命名空间中缺少哪些方法或重载以及如何实现它们。
首选可能使用现有方法的干净优雅的实现。
答案 0 :(得分:30)
/// <summary>Adds a single element to the end of an IEnumerable.</summary>
/// <typeparam name="T">Type of enumerable to return.</typeparam>
/// <returns>IEnumerable containing all the input elements, followed by the
/// specified additional element.</returns>
public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T element)
{
if (source == null)
throw new ArgumentNullException("source");
return concatIterator(element, source, false);
}
/// <summary>Adds a single element to the start of an IEnumerable.</summary>
/// <typeparam name="T">Type of enumerable to return.</typeparam>
/// <returns>IEnumerable containing the specified additional element, followed by
/// all the input elements.</returns>
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> tail, T head)
{
if (tail == null)
throw new ArgumentNullException("tail");
return concatIterator(head, tail, true);
}
private static IEnumerable<T> concatIterator<T>(T extraElement,
IEnumerable<T> source, bool insertAtStart)
{
if (insertAtStart)
yield return extraElement;
foreach (var e in source)
yield return e;
if (!insertAtStart)
yield return extraElement;
}
答案 1 :(得分:21)
我很惊讶没人提到MoreLINQ project。它由Jon Skeet启动,并在此过程中获得了一些开发人员。从项目页面:
LINQ to Objects缺少一些 理想的功能。
这个项目将增强LINQ to 具有额外方法的对象,在 保持精神的方式 LINQ。
查看Operators Overview wiki页面,了解已实施运营商的列表。
这是学习一些干净优雅的源代码的好方法。
答案 2 :(得分:16)
纯粹主义者没什么,但是它很有用!
public static void Each<T>(this IEnumerable<T> items, Action<T> action)
{
foreach (var i in items)
action(i);
}
答案 3 :(得分:14)
/// <summary>Creates a <see cref="Queue<T>"/> from an enumerable
/// collection.</summary>
public static Queue<T> ToQueue<T>(this IEnumerable<T> source)
{
if (source == null)
throw new ArgumentNullException("source");
return new Queue<T>(source);
}
/// <summary>Creates a <see cref="Stack<T>"/> from an enumerable
/// collection.</summary>
public static Stack<T> ToStack<T>(this IEnumerable<T> source)
{
if (source == null)
throw new ArgumentNullException("source");
return new Stack<T>(source);
}
答案 4 :(得分:13)
public static bool IsEmpty<T>(this IEnumerable<T> source)
{
return !source.Any();
}
答案 5 :(得分:12)
另外两个众所周知的SQL结构的C#等价物
/// <summary>
/// Determines if the source value is contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
/// <c>true</c> if the source value matches at least one of the possible values; otherwise, <c>false</c>.
/// </returns>
public static bool In<T>(this T value, params T[] values)
{
if (values == null)
return false;
if (values.Contains<T>(value))
return true;
return false;
}
/// <summary>
/// Determines if the source value is contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
/// <c>true</c> if the source value matches at least one of the possible values; otherwise, <c>false</c>.
/// </returns>
public static bool In<T>(this T value, IEnumerable<T> values)
{
if (values == null)
return false;
if (values.Contains<T>(value))
return true;
return false;
}
/// <summary>
/// Determines if the source value is not contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
/// <c>false</c> if the source value matches at least one of the possible values; otherwise, <c>true</c>.
/// </returns>
public static bool NotIn<T>(this T value, params T[] values)
{
return In(value, values) == false;
}
/// <summary>
/// Determines if the source value is not contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
/// <c>false</c> if the source value matches at least one of the possible values; otherwise, <c>true</c>.
/// </returns>
public static bool NotIn<T>(this T value, IEnumerable<T> values)
{
return In(value, values) == false;
}
答案 6 :(得分:9)
/// <summary>
/// Returns a sequence containing one element.
/// </summary>
public static IEnumerable<T> AsIEnumerable<T>(this T obj)
{
yield return obj;
}
<强>用法强>:
var nums = new[] {12, 20, 6};
var numsWith5Prepended = 5.AsIEnumerable().Concat(nums);
答案 7 :(得分:9)
基本上与string.Join
相同,但是:
能够在任何集合中使用它,而不仅仅是字符串集合(在每个元素上调用ToString
)
能够为每个字符串添加前缀和后缀。
作为扩展方法。我发现string.Join
很烦人,因为它是静态的,这意味着在一系列操作中它在词法上不是正确的顺序。
/// <summary>
/// Turns all elements in the enumerable to strings and joins them using the
/// specified string as the separator and the specified prefix and suffix for
/// each string.
/// <example>
/// <code>
/// var a = (new[] { "Paris", "London", "Tokyo" }).JoinString(", ", "[", "]");
/// // a contains "[Paris], [London], [Tokyo]"
/// </code>
/// </example>
/// </summary>
public static string JoinString<T>(this IEnumerable<T> values,
string separator = null, string prefix = null, string suffix = null)
{
if (values == null)
throw new ArgumentNullException("values");
using (var enumerator = values.GetEnumerator())
{
if (!enumerator.MoveNext())
return "";
StringBuilder sb = new StringBuilder();
sb.Append(prefix).Append(enumerator.Current.ToString()).Append(suffix);
while (enumerator.MoveNext())
sb.Append(separator).Append(prefix)
.Append(enumerator.Current.ToString()).Append(suffix);
return sb.ToString();
}
}
答案 8 :(得分:8)
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
{
var random = new Random();
return items.OrderBy(x => random.Next());
}
编辑:上述实施似乎存在几个问题。以下是@ LukeH的代码和来自@ck和@Strilanc的评论的改进版本。
private static Random _rand = new Random();
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
var items = source == null ? new T[] { } : source.ToArray();
var count = items.Length;
while(count > 0)
{
int toReturn = _rand.Next(0, count);
yield return items[toReturn];
items[toReturn] = items[count - 1];
count--;
}
}
答案 9 :(得分:8)
/// <summary>Sorts the elements of a sequence in ascending order.</summary>
public static IEnumerable<T> Order<T>(this IEnumerable<T> source)
{
return source.OrderBy(x => x);
}
答案 10 :(得分:7)
Min
仅返回指定表达式返回的最小值,但不返回给出此最小元素的原始元素。
/// <summary>Returns the first element from the input sequence for which the
/// value selector returns the smallest value.</summary>
public static T MinElement<T, TValue>(this IEnumerable<T> source,
Func<T, TValue> valueSelector) where TValue : IComparable<TValue>
{
if (source == null)
throw new ArgumentNullException("source");
if (valueSelector == null)
throw new ArgumentNullException("valueSelector");
using (var enumerator = source.GetEnumerator())
{
if (!enumerator.MoveNext())
throw new InvalidOperationException("source contains no elements.");
T minElem = enumerator.Current;
TValue minValue = valueSelector(minElem);
while (enumerator.MoveNext())
{
TValue value = valueSelector(enumerator.Current);
if (value.CompareTo(minValue) < 0)
{
minValue = value;
minElem = enumerator.Current;
}
}
return minElem;
}
}
答案 11 :(得分:7)
这是我刚才想到的一个很酷的一个。 (如果我只是想到它,也许它不是那么有用?但我想到了它,因为我对它有用。)重复循环一个序列以生成无限序列。这实现了Enumerable.Range
和Enumerable.Repeat
给你的东西,除了它可以用于任意(不像Range
)序列(与Repeat
不同):
public static IEnumerable<T> Loop<T>(this IEnumerable<T> source)
{
while (true)
{
foreach (T item in source)
{
yield return item;
}
}
}
用法:
var numbers = new[] { 1, 2, 3 };
var looped = numbers.Loop();
foreach (int x in looped.Take(10))
{
Console.WriteLine(x);
}
输出:
1 2 3 1 2 3 1 2 3 1
注意:我想你也可以通过以下方式完成此任务:
var looped = Enumerable.Repeat(numbers, int.MaxValue).SelectMany(seq => seq);
...但我认为Loop
更清楚。
答案 12 :(得分:6)
返回特定大小的块。 x.Chunks(2)
的{{1}}将返回两个包含1,2,3,4,5
和1,2
的数组。 3,4
将返回x.Chunks(2,true)
,1,2
和3,4
。
5
答案 13 :(得分:6)
/// <summary>
/// Returns the index of the first element in this <paramref name="source"/>
/// satisfying the specified <paramref name="condition"/>. If no such elements
/// are found, returns -1.
/// </summary>
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> condition)
{
if (source == null)
throw new ArgumentNullException("source");
if (condition == null)
throw new ArgumentNullException("condition");
int index = 0;
foreach (var v in source)
{
if (condition(v))
return index;
index++;
}
return -1;
}
答案 14 :(得分:6)
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items)
{
return new HashSet<T>(items);
}
答案 15 :(得分:5)
这是一个有争议的问题;我相信很多纯粹主义者会反对null
成功的“实例方法”。
/// <summary>
/// Returns an IEnumerable<T> as is, or an empty IEnumerable<T> if it is null
/// </summary>
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source)
{
return source ?? Enumerable.Empty<T>();
}
<强>用法:强>
foreach(var item in myEnumerable.EmptyIfNull())
{
Console.WriteLine(item);
}
答案 16 :(得分:5)
这个涉及一个自定义委托(可能已经使用了IParser<T>
接口,但我使用了委托,因为它更简单),用于将一系列字符串解析为一系列值,跳过解析失败的元素。
public delegate bool TryParser<T>(string text, out T value);
public static IEnumerable<T> Parse<T>(this IEnumerable<string> source,
TryParser<T> parser)
{
source.ThrowIfNull("source");
parser.ThrowIfNull("parser");
foreach (string str in source)
{
T value;
if (parser(str, out value))
{
yield return value;
}
}
}
用法:
var strings = new[] { "1", "2", "H3llo", "4", "five", "6", "se7en" };
var numbers = strings.Parse<int>(int.TryParse);
foreach (int x in numbers)
{
Console.WriteLine(x);
}
输出:
1 2 4 6
这个命名很棘手。我不确定Parse
是否是最佳选择(至少 简单),或者ParseWhereValid
之类的东西会更好。
答案 17 :(得分:5)
在每对连续元素之间插入一个元素。
/// <summary>Inserts the specified item in between each element in the input
/// collection.</summary>
/// <param name="source">The input collection.</param>
/// <param name="extraElement">The element to insert between each consecutive
/// pair of elements in the input collection.</param>
/// <returns>A collection containing the original collection with the extra
/// element inserted. For example, new[] { 1, 2, 3 }.InsertBetween(0) returns
/// { 1, 0, 2, 0, 3 }.</returns>
public static IEnumerable<T> InsertBetween<T>(
this IEnumerable<T> source, T extraElement)
{
return source.SelectMany(val => new[] { extraElement, val }).Skip(1);
}
答案 18 :(得分:5)
/// <summary>
/// Returns the first element of a sequence, or a default value if the
/// sequence contains no elements.
/// </summary>
/// <typeparam name="T">The type of the elements of
/// <paramref name="source"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable<T>"/> to return
/// the first element of.</param>
/// <param name="default">The default value to return if the sequence contains
/// no elements.</param>
/// <returns><paramref name="default"/> if <paramref name="source"/> is empty;
/// otherwise, the first element in <paramref name="source"/>.</returns>
public static T FirstOrDefault<T>(this IEnumerable<T> source, T @default)
{
if (source == null)
throw new ArgumentNullException("source");
using (var e = source.GetEnumerator())
{
if (!e.MoveNext())
return @default;
return e.Current;
}
}
/// <summary>
/// Returns the first element of a sequence, or a default value if the sequence
/// contains no elements.
/// </summary>
/// <typeparam name="T">The type of the elements of
/// <paramref name="source"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable<T>"/> to return
/// the first element of.</param>
/// <param name="predicate">A function to test each element for a
/// condition.</param>
/// <param name="default">The default value to return if the sequence contains
/// no elements.</param>
/// <returns><paramref name="default"/> if <paramref name="source"/> is empty
/// or if no element passes the test specified by <paramref name="predicate"/>;
/// otherwise, the first element in <paramref name="source"/> that passes
/// the test specified by <paramref name="predicate"/>.</returns>
public static T FirstOrDefault<T>(this IEnumerable<T> source,
Func<T, bool> predicate, T @default)
{
if (source == null)
throw new ArgumentNullException("source");
if (predicate == null)
throw new ArgumentNullException("predicate");
using (var e = source.GetEnumerator())
{
while (true)
{
if (!e.MoveNext())
return @default;
if (predicate(e.Current))
return e.Current;
}
}
}
答案 19 :(得分:4)
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();
}
答案 20 :(得分:4)
这是我的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;
}
}
答案 21 :(得分:4)
这是一个简单的函数,如果你有一个中等大小的数据集(例如,超过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));
}
注意:
答案 22 :(得分:4)
有效地确定一个IEnumerable<T>
是否至少包含/确切地/最多包含一定数量的元素。
public enum CountAssertion
{
AtLeast,
Exact,
AtMost
}
/// <summary>
/// Asserts that the number of items in a sequence matching a specified predicate satisfies a specified CountAssertion.
/// </summary>
public static bool AssertCount<T>(this IEnumerable<T> source, int countToAssert, CountAssertion assertion, Func<T, bool> predicate)
{
if (source == null)
throw new ArgumentNullException("source");
if (predicate == null)
throw new ArgumentNullException("predicate");
return source.Where(predicate).AssertCount(countToAssert, assertion);
}
/// <summary>
/// Asserts that the number of elements in a sequence satisfies a specified CountAssertion.
/// </summary>
public static bool AssertCount<T>(this IEnumerable<T> source, int countToAssert, CountAssertion assertion)
{
if (source == null)
throw new ArgumentNullException("source");
if (countToAssert < 0)
throw new ArgumentOutOfRangeException("countToAssert");
switch (assertion)
{
case CountAssertion.AtLeast:
return AssertCountAtLeast(source, GetFastCount(source), countToAssert);
case CountAssertion.Exact:
return AssertCountExact(source, GetFastCount(source), countToAssert);
case CountAssertion.AtMost:
return AssertCountAtMost(source, GetFastCount(source), countToAssert);
default:
throw new ArgumentException("Unknown CountAssertion.", "assertion");
}
}
private static int? GetFastCount<T>(IEnumerable<T> source)
{
var genericCollection = source as ICollection<T>;
if (genericCollection != null)
return genericCollection.Count;
var collection = source as ICollection;
if (collection != null)
return collection.Count;
return null;
}
private static bool AssertCountAtMost<T>(IEnumerable<T> source, int? fastCount, int countToAssert)
{
if (fastCount.HasValue)
return fastCount.Value <= countToAssert;
int countSoFar = 0;
foreach (var item in source)
{
if (++countSoFar > countToAssert) return false;
}
return true;
}
private static bool AssertCountExact<T>(IEnumerable<T> source, int? fastCount, int countToAssert)
{
if (fastCount.HasValue)
return fastCount.Value == countToAssert;
int countSoFar = 0;
foreach (var item in source)
{
if (++countSoFar > countToAssert) return false;
}
return countSoFar == countToAssert;
}
private static bool AssertCountAtLeast<T>(IEnumerable<T> source, int? fastCount, int countToAssert)
{
if (countToAssert == 0)
return true;
if (fastCount.HasValue)
return fastCount.Value >= countToAssert;
int countSoFar = 0;
foreach (var item in source)
{
if (++countSoFar >= countToAssert) return true;
}
return false;
}
<强>用法强>:
var nums = new[] { 45, -4, 35, -12, 46, -98, 11 };
bool hasAtLeast3Positive = nums.AssertCount(3, CountAssertion.AtLeast, i => i > 0); //true
bool hasAtMost1Negative = nums.AssertCount(1, CountAssertion.AtMost, i => i < 0); //false
bool hasExactly2Negative = nums.AssertCount(2, CountAssertion.Exact, i => i < 0); //false
答案 23 :(得分:4)
枚举长度为size
且包含最新值的数组(“windows”)
{ 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();
}
}
答案 24 :(得分:3)
ToList和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;
}
答案 25 :(得分:3)
与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);
}
答案 26 :(得分:3)
/// <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();
}
答案 27 :(得分:3)
Where
和IEnumerable
上的可选IQueryable
条款。在构建谓词时避免使用if语句&amp; lambda用于查询。在编译时不知道是否应该应用过滤器时很有用。
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");
答案 28 :(得分:2)
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;
}
答案 29 :(得分:2)
<强> EnumerableEx.OfThese 强>:
public static class EnumerableEx
{
public static IEnumerable<T> OfThese<T>(params T[] objects)
{
return objects;
}
}
轻松构建已知值的序列:
var nums=EnumerableEx.OfThese(1,2,3);
var nums2 = nums.Concat(EnumerableEx.OfThese(4));
答案 30 :(得分:2)
public 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
}
答案 31 :(得分:1)
与SingleOrDefault
类似,只有当列表中有多个元素时,它才会返回null而不是抛出异常。
public static T OneOrDefault<T>(this IEnumerable<T> list)
{
using (var e = list.GetEnumerator())
{
if (!e.MoveNext())
return default(T);
T val = e.Current;
if (e.MoveNext())
return default(T);
return val;
}
}
答案 32 :(得分:1)
这是我最喜欢的 - 很容易就任何集合进行广度优先搜索。在每次迭代时,该方法产生当前节点,其父节点,它在图形中的级别,以及它在广度优先顺序中的索引。这在某些情况下是很有帮助的,但是如果你不需要它,可以大大简化这个方法,如果你愿意 - 你只能生成节点本身。
public class IteratedNode<T>
{
public T Node;
public T ParentNode;
public int Level;
public int Index;
}
/// <summary>
/// Iterates over a tree/graph via In Order Breadth First search.
/// </summary>
/// <param name="root">The root item.</param>
/// <param name="childSelector">A func that receives a node in the tree and returns its children.</param>
public static IEnumerable<IteratedNode<T>> GetBreadthFirstEnumerable<T>(this T root, Func<T, IEnumerable<T>> childSelector)
{
var rootNode = new IteratedNode<T> { Node = root, ParentNode = default(T), Level = 1, Index = 1};
var nodesToProcess = new Queue<IteratedNode<T>>( new[] {rootNode});
int itemsIterated = 0;
while (nodesToProcess.Count > 0)
{
IteratedNode<T> currentItem = nodesToProcess.Dequeue();
yield return currentItem; itemsIterated++;
// Iterate over the children of this node, and add it to queue, to process later.
foreach (T child in childSelector(currentItem.Node))
{
nodesToProcess.Enqueue(
new IteratedNode<T> {
Node = child,
ParentNode = currentItem.Node,
Level = currentItem.Level + 1,
Index = itemsIterated
});
}
}
}
答案 33 :(得分:1)
这是一个非常简单的包装器,可以使某些查询更容易:
public static IEnumerable<T> Except<T>(this IEnumerable<T> elements, params T[] exceptions)
{
return elements.Except(exceptions);
}
用法:
//returns a list of "work week" DayOfWeek values.
Enum.GetValues(typeof(DayOfWeek)).Except(DayOfWeek.Saturday, DayOfWeek.Sunday);
这可以防止必须创建自己的集合以传递到Except,或者使用Where with a large Boolean“不等于A且不等于B和...”构造,使代码更清晰。
我也看过Except给出一个谓词,基本上它是Where的倒数,最简单的实现就是:
public static IEnumerable<T> Except<T>(this IEnumerable<T> elements, Predicate<T> predicate)
{
return elements.Where(x=>!predicate(x));
}
用法:
//best use is as a "natural language" statement already containing Where()
ListOfRecords.Where(r=>r.SomeValue = 123).Except(r=>r.Id = 2536);
如果C#同时包含do-while和do-until结构,那么它就有了它的位置。
答案 34 :(得分:1)
与Timwi的MinElement类似,但有一个更简洁的算法使用Aggregate扩展方法来遍历源Enumerable:
public static T ObjectWithMax<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection)
where TResult : IComparable<TResult>
{
if (elements == null) throw new ArgumentNullException("elements", "Sequence is null.");
if (!elements.Any()) throw new ArgumentException("Sequence contains no elements.");
//Set up the "seed" (current known maximum) to the first element
var seed = elements.Select(t => new {Object = t, Projection = projection(t)}).First();
//run through all other elements of the source, comparing the projection of each
//to the current "seed" and replacing the seed as necessary. Last element wins ties.
return elements.Skip(1).Aggregate(seed,
(s, x) =>
projection(x).CompareTo(s.Projection) >= 0
? new {Object = x, Projection = projection(x)}
: s
).Object;
}
public static T ObjectWithMin<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection)
where TResult : IComparable<TResult>
{
if (elements == null) throw new ArgumentNullException("elements", "Sequence is null.");
if (!elements.Any()) throw new ArgumentException("Sequence contains no elements.");
var seed = elements.Select(t => new {Object = t, Projection = projection(t)}).First();
//ties won by the FIRST element in the Enumerable
return elements.Aggregate(seed,
(s, x) =>
projection(x).CompareTo(s.Projection) < 0
? new {Object = x, Projection = projection(x)}
: s
).Object;
}
与OrderBy().First()
或Where(x=>x.SomeField == source.Min(someField)).First()
相比,我发现这些方法非常宝贵。它是线性的,只通过Enumerable一次,并且对列表的当前顺序是稳定的。
答案 35 :(得分:0)
C#相当于众所周知的SQL“之间”构造
/// <summary>
/// Determines if the current value is included in the range of specified values. Bounds are included.
/// </summary>
/// <typeparam name="T">The type of the values</typeparam>
/// <param name="val">The value.</param>
/// <param name="firstValue">The lower bound.</param>
/// <param name="secondValue">The upper bound.</param>
/// <returns>
/// Return <c>true</c> if the <paramref name="val">value</paramref> is between the <paramref name="firstValue"/> and the <paramref name="secondValue"/>; otherwise, <c>false</c>
/// </returns>
public static bool Between<T>(this T val, T firstValue, T secondValue) where T : IComparable<T>
{
if (val == null)
throw new ArgumentNullException();
if (firstValue == null ||
secondValue == null)
return false;
return firstValue.CompareTo(val) <= 0 && secondValue.CompareTo(val) >= 0;
}
答案 36 :(得分:0)
将Where
与SQL LIKE
模式匹配相结合。
public static IEnumerable<TSource> WhereLike<TSource>(this IEnumerable<TSource> source, Func<TSource, string> selector, string match)
{
/* Turn "off" all regular expression related syntax in
* the pattern string. */
string pattern = Regex.Escape(match);
/* Replace the SQL LIKE wildcard metacharacters with the
* equivalent regular expression metacharacters. */
pattern = pattern.Replace("%", ".*?").Replace("_", ".");
/* The previous call to Regex.Escape actually turned off
* too many metacharacters, i.e. those which are recognized by
* both the regular expression engine and the SQL LIKE
* statement ([...] and [^...]). Those metacharacters have
* to be manually unescaped here. */
pattern = pattern.Replace(@"\[", "[").Replace(@"\]", "]").Replace(@"\^", "^");
Regex reg = new Regex(pattern, RegexOptions.IgnoreCase);
return source.Where(t => reg.IsMatch(selector(t)));
}
答案 37 :(得分:0)
public static void AddRangeWhere<T>(this IEnumerable<T> list, IEnumerable<T> sourceList, Func<T, bool> predicate)
{
if (list != null)
{
List<T> newRange = new List<T>();
foreach (var item in sourceList)
{
if (predicate(item))
newRange.Add(item);
}
list.Concat(newRange);
}
}
答案 38 :(得分:0)
我的是RemoveConcurrent<T>()
:
public static IEnumerable<T> RemoveConcurrent<T>(this IEnumerable<T> ts) where T : IEquatable<T>
{
bool first = true;
T lval = default(T);
foreach (T t in ts)
{
if (first || !t.Equals(lval))
{
first = false;
yield return t;
}
lval = t;
}
}
可能有一种方法可以使用标准LINQ查询来执行此操作,但如果是这样,我不知道如何。
答案 39 :(得分:0)
/// <summary>
/// Performs the specified action on each element of the IEnumerable.
/// </summary>
public static void ForEachAction<T>(this IEnumerable<T> enumerable, Action<T> action)
{
if (action == null || enumerable == null)
{
throw new ArgumentNullException();
}
foreach (var item in enumerable)
{
action.Invoke(item);
}
}
答案 40 :(得分:0)
将根据条件将IEnumerable分为两个IList
public static void Partition<T>(this IEnumerable<T> source, Func<T, bool> predicate, ref IList<T> matches, ref IList<T> nonMatches)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (predicate == null)
throw new ArgumentNullException(nameof(source));
var _matches = new List<T>();
var _nonMatches = new List<T>();
foreach (var itm in source)
{
if (predicate(itm))
_matches.Add(itm);
else
_nonMatches.Add(itm);
}
if (matches == null)
matches = new List<T>();
else
matches.Clear();
if (nonMatches == null)
nonMatches = new List<T>();
else
nonMatches.Clear();
foreach (var m in _matches)
matches.Add(m);
nonMatches.Clear();
foreach (var m in _nonMatches)
nonMatches.Add(m);
nonMatches = _nonMatches;
}
答案 41 :(得分:-1)
public static IEnumerable<T> Append<T>(this IEnumerable<T> enumerable, T item)
{
return enumerable.Concat(new[] { item });
}
public static IEnumerable<T> AppendIf<T>(this IEnumerable<T> enumerable, T item, Func<bool> predicate)
{
return predicate() ? enumerable.Append(item) : enumerable;
}
答案 42 :(得分:-1)
public static string Join<T>(this IEnumerable<T> items, string delimiter)
{
var result = new StringBuilder();
if (items != null && items.Any())
{
delimiter = delimiter ?? String.Empty;
foreach (var item in items)
{
result.Append(item);
result.Append(delimiter);
}
result.Length = result.Length - delimiter.Length;
}
return result.ToString();
}