5

I have a list of objects of the same type that each has a property that is an array of floats. Taking this list as input, what is the easiest way to return a new array that is the average of the arrays in the list?

The input objects look like this:

Class MyDatas
{
    float [] DataValues;
}

...

List<MyDatas> InputList;
float [] result;

The DataValues arrays will be guaranteed to have the same length. Each element of the result array will be the average of the corresponding elements in the array of each member of the list. For example: result[0] = (InputList[0].DataValues[0] + InputList[1].DataValues[0] + ... ) / InputList.Count

Of course I could brute force this with a foreach inside a for loop and then another for loop after that but it seems to me this should be doable with one or two lines of LINQ. Any advice?

Kirill Polishchuk
  • 54,804
  • 11
  • 122
  • 125
Segfault
  • 8,036
  • 3
  • 35
  • 54
  • Sum can take out one for-loop, so only 1 remaining:for (int i = 0; i < result.Length; i++) result[i] = inputList[0].DataValues.Sum() / result.Length; – MrFox Jul 12 '11 at 18:44

3 Answers3

5
float[] result = inputList.Select(c => c.DataValues)
                          .Transpose()
                          .Select(r => r.Average())
                          .ToArray();

using Transpose from here.

Community
  • 1
  • 1
dtb
  • 213,145
  • 36
  • 401
  • 431
3

Pure LINQ:

var result = InputList
    .SelectMany(x => x.DataValues.Select((y, i) => new { y, i }))
    .GroupBy(a => a.i, b => b.y, (k, v) => v.Average());

Sample:

List<MyDatas> InputList = new List<MyDatas>();
InputList.Add(new MyDatas { DataValues = new float[] { 1, 2, 3 } });
InputList.Add(new MyDatas { DataValues = new float[] { 4, 5, 6 } });
InputList.Add(new MyDatas { DataValues = new float[] { 7, 8, 9 } });

var result = InputList
    .SelectMany(x => x.DataValues.Select((y, i) => new { y, i }))
    .GroupBy(a => a.i, b => b.y, (k, v) => v.Average());


foreach (var item in result)
{
    Console.WriteLine(item);
}

Output:

4
5
6
Kirill Polishchuk
  • 54,804
  • 11
  • 122
  • 125
2

How about this?

var averages = (from index in Enumerable.Range(0, InputList[0].DataValues.Length)
                select (from item in InputList
                        select item.DataValues[index]).Average()).ToArray();

Or, using fluent syntax:

var averages = Enumerable.Range(0, InputList[0].DataValues.Length)
               .Select(index => InputList.Average(item => item.DataValues[index]))
               .ToArray();

It clearly shows that what you're really doing is computing a result for each index in the DataValues arrays.

Aaron
  • 2,013
  • 16
  • 22