Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何获取列表的子集<;T>;基于对象属性总和的项_C#_Linq - Fatal编程技术网

C# 如何获取列表的子集<;T>;基于对象属性总和的项

C# 如何获取列表的子集<;T>;基于对象属性总和的项,c#,linq,C#,Linq,我希望根据一个对象属性的值从列表中获取对象的子集,特别是我希望根据该属性的聚合值的总和获取前几个对象 我可以手动迭代列表,添加/求和属性的值,并将结果与我想要的值进行比较,但是有更好的方法吗 例如,假设我有以下列表: List<MyObj> MyObjList; MyObjList按以下顺序具有以下对象和值: MyObjList[0].MyValue = 1; MyObjList[1].MyValue = 3; MyObjList[2].MyValue = 2; MyObjList

我希望根据一个对象属性的值从列表中获取对象的子集,特别是我希望根据该属性的聚合值的总和获取前几个对象

我可以手动迭代列表,添加/求和属性的值,并将结果与我想要的值进行比较,但是有更好的方法吗

例如,假设我有以下列表:

List<MyObj> MyObjList;
MyObjList按以下顺序具有以下对象和值:

MyObjList[0].MyValue = 1;
MyObjList[1].MyValue = 3;
MyObjList[2].MyValue = 2;
MyObjList[3].MyValue = 3;
MyObjList[4].MyValue = 2;

例如,我可能想得到前几个项目,其中MyValue的总和是聚合和TakeWhile的组合,所以让我们写下它

public static IEnumerable<S> AggregatingTakeWhile<S, A>(
  this IEnumerable<S> items,
  A initial,
  Func<A, S, A> accumulator,
  Func<A, S, bool> predicate) 
{
  A current = initial;
  foreach(S item in items)
  {
    current = accumulator(current, item);
    if (!predicate(current, item))
      break;
    yield return item;
  }
}
公共静态IEnumerable AggregatingTakeWhile(
这是数不清的项目,
首字母,
Func累加器,
Func谓词)
{
A电流=初始电流;
foreach(项目中的项目)
{
电流=蓄能器(电流,项目);
if(!谓词(当前,项))
打破
收益回报项目;
}
}
现在你可以说

var items = myObjList.AggregatingTakeWhile(
  0,
  (a, s) => a + s.MyValue,
  (a, s) => a <= 5);
var items=myObjList.AggregatingTakeWhile(
0,
(a,s)=>a+s.MyValue,
(a,s)=>a+s.MyValue)
.TakeWhile(((a,s))=>a;

我可能把元组语法搞错了;我现在手头没有Visual Studio。但你明白了。聚合生成一个(和、项)元组序列,现在我们可以在该序列上使用普通序列运算符。

对于一种老式的非Linq方法,您可以为此编写一个简单的方法:

static List<MyObj> GetItemsUntilSumEquals(List<MyObj> items, int maxSum)
{    
    if (items == null) return null;

    var result = new List<MyObj>();
    var sum = 0;

    foreach (var item in items)
    {
        if (sum + item.MyValue > maxSum) break;
        sum += item.MyValue;
        result.Add(item);
    }

    return result;
}
静态列表GetItemsUntilSumEquals(列表项,int-maxSum)
{    
如果(items==null)返回null;
var result=新列表();
var总和=0;
foreach(项目中的var项目)
{
如果(sum+item.MyValue>maxSum)中断;
总和+=item.MyValue;
结果.添加(项目);
}
返回结果;
}

我提供的非常优雅的解决方案(+1)。我正要发布几乎相同的解决方案。。。有点太慢了!出于好奇,我将
谓词
简单地实现为
谓词
。你选择
Func
有什么特别的原因吗?我喜欢这个答案的一个原因是它让我接触到了一些我以前从未使用过的C#(Func)。@介于两者之间:C#团队的普遍态度是谓词等都是不必要的错误特性。“谓词”是自文档化的,因为它告诉您“这个返回bool的委托用于做出决策”,因此它在逻辑上不同于从文件反序列化bool的委托。这很好,但我们真的需要在类型系统中进行区分吗?我们真的想让手头有一个
谓词
的人需要一个
Func
来进行转换吗?不需要。只要在任何地方都坚持使用
Func
。@中间:现在,如果你的问题是为什么
A,S
,而不仅仅是
A
——你可能会想要类似“拿走这些物品,直到一个物品的大小是目前为止运行平均值的两倍”之类的东西。在这种情况下,您需要在谓词中包含当前项和累加器。您可以添加某种类型的
int currentSum
,以避免在每次迭代中重新计算
result.Sum()
。这也导致了一个“无Linq”的解决方案。@MartinBackasch非常正确!这比所有的总和都好。
public static IEnumerable<(A, S)> RunningAggregate<S, A>(
  this IEnumerable<S> items,
  A initial,
  Func<A, S, A> accumulator) 
{
  A current = initial;
  foreach(S item in items)
  {
    current = accumulator(current, item);
    yield return (current, item);
  }
}
var result = myObjList
  .RunningAggregate(0, (a, s) => a + s.MyValue)
  .TakeWhile( ((a, s)) => a <= 5)
  .Select(((a, s)) => s);
static List<MyObj> GetItemsUntilSumEquals(List<MyObj> items, int maxSum)
{    
    if (items == null) return null;

    var result = new List<MyObj>();
    var sum = 0;

    foreach (var item in items)
    {
        if (sum + item.MyValue > maxSum) break;
        sum += item.MyValue;
        result.Add(item);
    }

    return result;
}