LINQ - 根据数组B上的值从数组A中选择元素

时间:2012-05-17 16:05:28

标签: arrays linq select

假设我们有两个数组:

DateTime[] wDates = new DateTime[20000];
double[] wValues = new double[20000];

这两个数组都按顺序排序,给定一个int i,wValues [i]用于日期wDates [i]。

让我们说我们需要获得日期月份为1月的wValues的平均值

使用标准循环,这将是:

double wAvg = 0.0;
int wDataCount = 0;
for (int i=0; i < 20000; i++)
  if (wDates[i].Month == 1)
  {
    wAvg += wValues[i];
    wDataCount++;
  }

if (wDataCount > 0)
  wAvg /= wDataCount;

我想知道如何在LINQ中执行此操作? 我可以创建一个包含两个值的结构/类DateDouble,然后执行以下操作:

List<DateDouble> wListData = new List<DateDouble>();
Add the items...
double wAvg = (from d in wListData
               where d.Date.Month == 1
               select d.Value).Average();

但是,当每天完成数千万次时,创建数千个DateDouble对象将是一个巨大的内存开销。对于临时对象也会发生同样的情况,尝试使用'index'并在数组上加入索引会产生糟糕的性能。

在LINQ中有更好的方法来实现这一目标吗?

谢谢, MM

3 个答案:

答案 0 :(得分:3)

好吧,您可以使用Zip运算符来简化操作:

var average = wDates.Zip(wValues, (date, value) => new { date, value })
                    .Where(pair => pair.date.Month == 1)
                    .Average(pair => pair.value);

那仍然会为每对创建一个匿名类型的实例,但我会亲自让它去衡量性能假设它将太昂贵。请注意,这将以流式方式运行 - 因此虽然它会产生大量垃圾,但任何时候所需的总内存都很小。

你可以通过创建自己的对 struct 来提高效率,避免创建额外的对象,但这会更加困难。不过坏了,但是:

// The normal Tuple types are classes.
public struct TupleValue<T1, T2>
{
    private readonly T1 item1;
    private readonly T2 item2;

    public T1 Item1 { get { return item1; } }
    public T2 Item2 { get { return item2; } }

    public TupleValue(T1 item1, T2 item2)
    {
        this.item1 = item1;
        this.item2 = item2;
    }
}

var average = wDates.Zip(wValues, (date, value) => 
                                   new TupleValue<DateTime, double>(date, value))
                    .Where(pair => pair.Item1.Month == 1)
                    .Average(pair => pair.Item2);

我只会在之后证明第一种方法是两个昂贵​​的。

答案 1 :(得分:2)

有一个IEnumerable.Where()扩展方法的重载版本,它也考虑了谓词中的索引。

double average = wValues.Where((d, i) => wDates[i].Month == 1).Average();

答案 2 :(得分:0)

类似的东西:

double wAvg = wDates.Select((d,i) => new { Month = d.Month, Index = i })
                    .Where(x => x.Month == 1)
                    .Select(x => wValues[i])
                    .Average();

无论如何,在这种情况下,您还将创建该匿名类型的N个实例。