4

I have a list of lists of dynamic which is currently being filtered through this:

var CPUdataIWant = from s in rawData
                   where s.stat.Contains("CPU")
                   select s;

//CPUDataIWant is a List<List<dynamic>>.

I have 86000 values in each inner list.

And what I need to do, is group the values into groups of 3, select the max of that group, and insert that into another list of List of dynamic, or just filter it out of CPUDataIWant.

So an example of what I want would be:

Raw data = 14,5,7,123,5,1,43,87,9

And my processed value would be:

ProceData = [14,5,7], [123,5,1], [43,87,9]
ProceData = [14,123,87]

Doesn't have to be using linq but the easier the better.

EDIT: Ok I explained what a wanted a bit poorly.

here's what I have:

List<List<object>>

In this List, I'll have X amount of Lists called A. In A I'll have 86000 values, let's say they're ints for now.

What I'd like, is to have

List<List<object>>

But instead of 86000 values in A, I want 28700, which would be made from the max of every 3 values in A.

ekad
  • 14,436
  • 26
  • 44
  • 46
Chris Gregori
  • 65
  • 1
  • 1
  • 8
  • 1
    Come on, that's easy. Iterate the collection in steps of three. Check i, i+1, i+2, pick the biggest. Add some boundary checks. Finished... – DHN Jul 26 '12 at 13:49
  • 1
    Can you elaborate a little bit, How can RawData be 14,5,7,123,5,1,43,87,9?! didn't you say it is a list of lists? – Vitaliy Jul 26 '12 at 14:12
  • Good question, I was basing my answer on the example from `data`. – phant0m Jul 26 '12 at 14:13

4 Answers4

1
IEnumerable<int> filtered = raw.Select((x, i) => new { Index = i, Value = x }).
    GroupBy(x => x.Index / 3).
    Select(x => x.Max(v => v.Value));

or, if you plan to use it more often

public static IEnumerable<int> SelectMaxOfEvery(this IEnumerable<int> source, int n)
{
    int i = 0;
    int currentMax = 0;
    foreach (int d in source)
    {
        if (i++ == 0)
            currentMax = d;
        else
            currentMax = Math.Max(d, currentMax);
        if (i == n)
        {
            i = 0;
            yield return currentMax;
        }
    }
    if (i > 0)
        yield return currentMax;
}

//...

IEnumerable<int> filtered = raw.SelectMaxOfEvery(3);
voidengine
  • 2,504
  • 1
  • 17
  • 29
0

This should give the desired result:

var data = new List<dynamic> { 1, 2, 3,   3, 10, 1,   5, 2, 8 };
var firsts = data.Where((x, i) => i % 3 == 0);
var seconds = data.Where((x, i) => (i + 2) % 3 == 0);
var thirds = data.Where((x, i) => (i + 1) % 3 == 0);
var list = firsts.Zip(
    seconds.Zip(
        thirds, (x, y) => Math.Max(x, y)
    ), 
    (x, y) => Math.Max(x, y)
).ToList();

List now contains:

3, 10, 8

Or generalized to an extension method:

public static IEnumerable<T> ReduceN<T>(this IEnumerable<T> values, Func<T, T, T> map, int N)
{
    int counter = 0;
    T previous = default(T);
    foreach (T item in values)
    {
        counter++;
        if (counter == 1)
        {
            previous = item;
        }
        else if (counter == N)
        {
            yield return map(previous, item);
            counter = 0;
        }
        else
        {

            previous = map(previous, item);
        }
    }

    if (counter != 0)
    {
        yield return previous;
    }
}

Used like this:

data.ReduceN(Math.Max, 3).ToList()
phant0m
  • 16,595
  • 5
  • 50
  • 82
  • @Hogan: Can you clarify why? It runs on 1 million elements in ~80ms including the conversion to a list. – phant0m Jul 26 '12 at 15:01
  • The first example (the only code you had when I made the comment) runs at O(3n) instead of O(n), I've not looked at the second. – Hogan Jul 26 '12 at 15:58
0

Old-school way of doing things makes it quite simple (although it's not as compact as LINQ):

// Based on this spec: "CPUDataIWant is a List<List<dynamic>>"
// and on the example, which states that the contents are numbers.
//
List<List<dynamic>> filteredList = new List<List<dynamic>>();
foreach (List<dynamic> innerList in CPUDataIWant)
{
    List<dynamic> innerFiltered = new List<dynamic>();

    // if elements are not in multiples of 3, the last one or two won't be checked.
    for (int i = 0; i < innerList.Count; i += 3)
    {   
        if(innerList[i+1] > innerList[i])
            if(innerList[i+2] > innerList[i+1])
                innerFiltered.Add(innerList[i+2]);
            else
                innerFiltered.Add(innerList[i+1]);
        else
            innerFiltered.Add(innerList[i]);
    }
    filteredList.Add(innerFiltered);
}
Alex
  • 23,004
  • 4
  • 39
  • 73
0

If you felt a need to use Aggregate you could do it like this:

(tested wiht LinqPad)

class Holder
{
       public dynamic max = null;
       public int count = 0;
}

void Main()
{
    var data = new List<dynamic>
        {new { x = 1 }, new { x = 2 }, new { x = 3 }, 
         new { x = 3 }, new { x = 10}, new { x = 1 }, 
         new { x = 5 }, new { x = 2 }, new { x = 1 },
         new { x = 1 }, new { x = 9 }, new { x = 3 }, 
         new { x = 11}, new { x = 10}, new { x = 1 }, 
         new { x = 5 }, new { x = 2 }, new { x = 12 }};

    var x = data.Aggregate(
       new LinkedList<Holder>(),
       (holdList,inItem) => 
       {
          if ((holdList.Last == null) || (holdList.Last.Value.count == 3))
          {
            holdList.AddLast(new Holder { max = inItem, count = 1});
          }
          else
          {
            if (holdList.Last.Value.max.x < inItem.x)
               holdList.Last.Value.max = inItem;

            holdList.Last.Value.count++;
          }
          return holdList;
       },
       (holdList) => { return holdList.Select((h) => h.max );} );

    x.Dump("We expect 3,10,5,9,11,12");
}
Hogan
  • 69,564
  • 10
  • 76
  • 117