LINQ - returning list of averages based on index in list

So I have a kindda odd scenario that I cannot get my head arround:

I have a outerlist which contains multiple innerlists.
Each innerlist contains multiple DataPoint objects.
Every innerlist contains the same amount of DataPoint objects.
A datapoint has a field called Value which is a double - e.g.:

So what I wanna do is to "combine" the innerlists into one List where the value of each DataPoint should be the average of the old values, based on its index in the list - in this case:

Is there any neat linq expression that could perform this kind of stuff (Im betting there are :))?


I ended up using the following solution:

var averages = Enumerable.Range(0, outer.First().Count()).Select(i => new DataPoint(outer.Select(l => l[i]).Average(x=>x.Value)));

which outputs IEnumerable, now with average valus - yay!

Upvotes: 3

Views: 686

Answers (4)

Michael Hays
Michael Hays

Reputation: 6908

This is all you need:

var outer = new List<List<List<double>>>();
var l1 = outer.Select(
  x => new List<double> { 
         x.Average(y => y[0]), 
         x.Average(y => y[1]), 
         x.Average(y => y[2])
       });

With a data structure like yours, it would be:

var list = items.Select(x => new List<DataPoint> (
            Enumerable.Range(0,3).Select(z => 
                new DataPoint {
                   Value = x.InnerLists.Average(y => y.DataPoints[z].Value)})
           ));

(Thank you to whomever suggested the Enumerable.Range)

Upvotes: 0

user180326
user180326

Reputation:

I'm on my iPad, so I havent compiled, but the following should work:

Enumerable.Range(0,outer.First().Length).
   Select(i => outer.Select(l => l[i]).Average());

Upvotes: 1

jason
jason

Reputation: 241641

Start with an extension method to take a slice of the inner lists:

public static IEnumerable<double> Slice(
    this IEnumerable<List<DataPoint>> innerLists,
    int index) {
        foreach(var innerList in innerLists) {
            yield return innerList.DataPoints[index].Value;
        }
    }
}

Then:

var averages = Enumerable.Range(0, count)
                         .Select(index => outerList.InnerLists.Slice(index))
                         .Select(slice => slice.Average());

I am assuming a hierarchy like this:

class DataPoint { public double Value { get; set; } }

class InnerList { public List<DataPoint> DataPoints { get; set; } }

class OuterList { public IEnumerable<InnerList> InnerLists { get; set; } }

but the above idea is obviously adaptable to however exactly you have structured your data.

Upvotes: 3

driis
driis

Reputation: 164291

The key here would be to group the inner list values by their index. Something like this would work:

// Test data
var list = new List<List<double>>() { 
    new List<double> { 2, 2 },
    new List<double> { 4, 5 },
    new List<double> { 3, 5 }
};

// Created groups based on index
var lookup = from inner in list 
             from value in inner.Select((n,idx) => new { n,idx })
             group value by value.idx;

// Calculate averages    
var averagesBasedOnIndex = lookup.Select(g => g.Average(x => x.n)).ToList();

Upvotes: 2

Related Questions