我想做这样的事情(如下),但不确定是否有正式/优化语法这样做?
.Orderby(i => i.Value1)
.Take("Bottom 100 & Top 100")
.Orderby(i => i.Value2);
基本上,我想按一个变量排序,然后取前100和下100,然后用另一个变量对这些结果进行排序。
有什么建议吗?
答案 0 :(得分:26)
var sorted = list.OrderBy(i => i.Value);
var top100 = sorted.Take(100);
var last100 = sorted.Reverse().Take(100);
var result = top100.Concat(last100).OrderBy(i => i.Value2);
我不知道你最后是想要Concat
还是Union
。 Concat
将组合两个列表的所有条目,即使存在类似的条目,如果原始列表包含少于200个条目也是如此。 Union
只会添加last100中尚未包含在top100中的内容。
有些事情不明确但应该考虑:
如果列表是数据库的IQueryable
,建议您使用ToArray()
或ToList()
,例如。
var sorted = list.OrderBy(i => i.Value).ToArray();
一开始。这样,只有一个数据库查询完成,其余查询在内存中完成。
Reverse
方法没有按照我希望的方式进行优化,但它不应该是一个问题,因为在这里排序列表是真正的优惠。但是,对于记录,其他答案中解释的跳过方法可能要快一点,但需要知道列表中的元素数量。
如果列表是LinkedList
或其他实施IList
的类,则Reverse
方法可以以优化的方式完成。
答案 1 :(得分:6)
您可以使用这样的扩展方法:
public static IEnumerable<T> TakeFirstAndLast<T>(this IEnumerable<T> source, int count)
{
var first = new List<T>();
var last = new LinkedList<T>();
foreach (var item in source)
{
if (first.Count < count)
first.Add(item);
if (last.Count >= count)
last.RemoveFirst();
last.AddLast(item);
}
return first.Concat(last);
}
(我LinkedList<T>
使用last
,因为它可以移除O(1)
中的项目
你可以像这样使用它:
.Orderby(i => i.Value1)
.TakeFirstAndLast(100)
.Orderby(i => i.Value2);
请注意,它不会处理少于200个项目的情况:如果是这样的话,您将获得重复项。如有必要,您可以使用Distinct
删除它们。
答案 2 :(得分:4)
分别取出前100名和后100名并将它们联合起来:
var tempresults = yourenumerable.OrderBy(i => i.Value1);
var results = tempresults.Take(100);
results = results.Union(tempresults.Skip(tempresults.Count() - 100).Take(100))
.OrderBy(i => i.Value2);
答案 3 :(得分:3)
如果您有可用元素的数量,您也可以使用此.Where
重载在一个语句中执行此操作:
var elements = ...
var count = elements.Length; // or .Count for list
var result = elements
.OrderBy(i => i.Value1)
.Where((v, i) => i < 100 || i >= count - 100)
.OrderBy(i => i.Value2)
.ToArray(); // evaluate
以下是它的工作原理:
| first 100 elements | middle elements | last 100 elements |
i < 100 i < count - 100 i >= count - 100
答案 4 :(得分:3)
您可以编写自己的扩展方法,例如Take()
,Skip()
以及Enumerable
类中的其他方法。它将元素的数量和列表中的总长度作为输入。然后它将返回序列中的第一个和最后一个 N 元素。
var result = yourList.OrderBy(x => x.Value1)
.GetLastAndFirst(100, yourList.Length)
.OrderBy(x => x.Value2)
.ToList();
这是扩展方法:
public static class SOExtensions
{
public static IEnumerable<T> GetLastAndFirst<T>(
this IEnumerable<T> seq, int number, int totalLength
)
{
if (totalLength < number*2)
throw new Exception("List length must be >= (number * 2)");
using (var en = seq.GetEnumerator())
{
int i = 0;
while (en.MoveNext())
{
i++;
if (i <= number || i >= totalLength - number)
yield return en.Current;
}
}
}
}