2012-05-17 52 views
2

设说值,我们有两个数组:LINQ - 选择从阵列元件A根据在阵列乙

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

这两个阵列被两个顺序排列,即给定一个int I,wValues [I]是日期wDates [i]。

我们可以说,我们需要得到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中? 我可以创建一个struct /类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对象将是一个很大的内存开销。临时对象也会发生同样的情况,并尝试使用'索引',并加入数组中的索引会产生可怕的表现。

在LINQ中有更好的方法来实现这个吗?

感谢, MM

回答

2

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

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

谢谢。这是一个非常优雅的解决方案! –

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); 

我只会做这个证明了第一种方法是两本很贵的,虽然之后。

+0

您必须马上出现。 :P +1 –

+0

为什么不使用KeyValuePair ? – ivowiblo

+0

@ivowiblo:因为它不是一个真正的键/值对吗?是的,它会起作用 - 但它会给人一种印象,即日期是某种关键,而我们没有任何证据表明这个问题。它*可能是一个关键,但它可能不是。 –

0

喜欢的东西:

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

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

+0

谢谢。是的,我希望避免创建匿名对象。 –

+0

在这种情况下,您应该保留已经优化的代码。 – ivowiblo