使用LINQ占据前100名和后100名?

时间:2014-08-03 09:51:28

标签: c# linq

我想做这样的事情(如下),但不确定是否有正式/优化语法这样做?

.Orderby(i => i.Value1)
.Take("Bottom 100 & Top 100")
.Orderby(i => i.Value2);

基本上,我想按一个变量排序,然后取前100和下100,然后用另一个变量对这些结果进行排序。

有什么建议吗?

5 个答案:

答案 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还是UnionConcat将组合两个列表的所有条目,即使存在类似的条目,如果原始列表包含少于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;
            }
        }
    }
}