LINQ - 根据字段的总和在集合中查找项目

时间:2012-07-14 04:53:30

标签: c# linq

这个问题有点难以解释,但我会尽量简短。

我的情况是我有一组键/值对,比如这个。

class Foo {
   int Number { get; set; }
   int Value { get; set; }
}

所以我会像这样设置一个集合...

var collection = new List<Foo>{
   new Foo { Number = 1, Value = 1 },
   new Foo { Number = 2, Value = 2 },
   new Foo { Number = 3, Value = 3 },
   new Foo { Number = 4, Value = 4 }
};

所以我希望基本上能够找到所有先前值的总和小于或等于给定值的数字。例如......

如果给定值3,则返回的数字应为collection[1]。 (collection[0]的值为1,collection[1]的值为2,总计为3)

我可以使用几个不同的行来实现这一点,但我想知道LINQ是否能够以更简洁的方式完成它。

4 个答案:

答案 0 :(得分:1)

不,LINQ并不适合这样做。这是可能的,但您基本上必须使用Aggregate函数并提供自己的聚合创建委托。这样做更简单(更不用说更容易理解)了:

int sum = 0;

foreach(var item in collection)
{    
    sum += item.Value;

    if (sum >= targetValue) return item;
}

return null;

答案 1 :(得分:0)

LINQ通常不会鼓励元素之间的相互依赖(在这种情况下,每个元素都需要能够看到元素之前的元素之和)。

编写一个循环来执行此操作会更容易,更清晰。您可以将它放在扩展方法后面以保持功能感:

public static Foo FirstBeyondSum(this IEnumerable<Foo> source, int value)
{
    var sum = 0;

    foreach(var item in source)
    {
        sum += item.Value;

        if(sum >= value)
        {
            return item;
        }
    }

    return null;
}

用法:

var firstFooBeyond3 = foos.FirstBeyondSum(3);

答案 2 :(得分:0)

我不知道为什么人们一直告诉你LINQ不适合这个......

    var sum = 0;
    return collection.FirstOrDefault(item => (sum += item.Value) >= targetValue);

也许是&gt;而不是&gt; =取决于边缘情况,但你明白了......

或者:

return collection.FirstOrDefault(item =>
    {
        sum += item.Value;
        return (sum += item.Value) >= targetValue;
    });

如果您不喜欢(sum + = item.Value)&gt; = targetValue hack ...

答案 3 :(得分:0)

确切的问题是:

  

我可以使用几个不同的线来完成这个,但我是   想知道LINQ能否以更简洁的方式做到这一点。

OP已经通过而不是使用LINQ解决了这个问题,并且想知道是否有更好的LINQ解决方案。因此,他希望能够看到LINQ解决方案,这样他就可以评估它是否比不使用LINQ更能解决他的问题。

所以,这是我能找到解决问题的LINQ解决方案的最佳方法。即使它的效率低于他的代码,这仍然是他的问题的答案,即LINQ是否能够更简洁地完成它,从而回答他的确切问题。

此外,OP可以评估是否使用LINQ的最佳方式是举例。我不明白他为什么不能使用LINQ。

从集合中获取项目:

var item = collection
    .Where((f,i) => value >= collection.Take(i + 1).Sum (c => c.Value))
    .Last();

如果您正在寻找该项目的索引:

var index = collection.IndexOf(
     collection.Where((f,i) => value >= collection.Take(i + 1).Sum (c => c.Value))
    .Last());