I have a List where each double[] has a length of 3. I would like to clean this list by leaving only those double[] having unique elements within a given tolerance (round up). For instance, a list like the one below:
1059.17 0 446.542225842081
1059.17 0 446.542564789741
1059.17 0 446.541759880305
959.167 0 579.827860527898
959.167 0 579.827847296075
Should become this for a given tolerance=two:
1059.17 0 446.54,
959.17 0 579.83,
Is there a smart way to do this in a neat way?
This should work. It uses the build-in equality comparisons of anonymous types.
List<double[]> data = ...
int tolerance = 2;
var roundedData = data
.Select(x => new {
v1 = Math.Round(x[0], tolerance),
v2 = Math.Round(x[1], tolerance),
v3 = Math.Round(x[2], tolerance)
})
.Distinct()
.Select(x => new [] { x.v1, x.v2, x.v3 })
.ToList();
Providing that array elements are always in the same order you can create your own comparer that should know how to compare double arrays :
public class MyDoubleArrComparer : IEqualityComparer<double[]>
{
public bool Equals(double[] x, double[] y)
{
for (int i = 0; i < x.Length; i++)
{
if (x[i] != y[i]) return false;
}
return true;
}
public int GetHashCode(double[] obj)
{
return base.GetHashCode();
}
}
And you can create a helper method that will round numbers and remove duplicates :
public static class Helper
{
public static List<double[]> MyFilter(this List<double[]> list, int tolerance)
{
var result = list
.Select(arr =>
{
// rounds numbers with precision that is set in tolerance variable
arr = arr.Select(d => d = Math.Round(d, tolerance)).ToArray();
return arr;
}).Distinct(new MyDoubleArrComparer()) // here we use our custom comparer
.ToList();
return result;
}
}
Now we can start using our helper method :
var nums = new List<double[]>()
{
new[] {1059.17, 0, 446.542225842081},
new[] {1059.17, 0, 446.542564789741},
new[] {1059.17, 0, 446.541759880305},
new[] {959.167, 0, 579.827860527898},
new[] {959.167, 0, 579.827847296075},
};
var result = nums.MyFilter(2);
foreach (var arr in result)
{
foreach (var d in arr)
{
Console.Write(d + " ");
}
Console.WriteLine();
}
Output :
1059.17 0 446.54
959.17 0 579.83
Maybe this will work?
public List<double[]> CleanWithTolerance(List<double[]> doubleNumbersList, int tolerance)
{
var newDoublesNumbersList = new List<double[]>();
foreach(double[] doubleNumbers in doubleNumbersList)
{
var newDoublesNumbers = doubleNumbers.Select(doubleNumber => Math.Round(doubleNumber, tolerance)).ToArray();
if(newDoublesNumbersList.All(cleanDoubleNumbers => !Enumerable.SequenceEqual(cleanDoubleNumbers, newDoublesNumbers))
{
newDoublesNumbersList.Add(newDoublesNumbers);
}
}
return newDoublesNumbersList;
}
Related
I would like to ask you if there's a way by Linq to check discontinuity of multiple ranges, for example we have a class AgeRange:
public class AgeRange
{
public int firstValue {get;set;}
public int secondValue {get;set;}
}
var ageRange1 = new AgeRange(0,2); // interval [0,2]
var ageRange2 = new AgeRange(4,10); // interval [4,10]
var ageRange3 = new AgeRange(11,int.MaxValue); // interval [11,+oo[
var ageRangeList = new List<AgeRange>();
ageRangeList.Add(ageRange1);
ageRangeList.Add(ageRange2);
ageRangeList.Add(ageRange3);
in this example we have a discontinuity between first range and second range.
is there a way in Linq to check discontinuity between elements in ageRangeList ?
Thanks for you help.
Assuming firstValue always <= secondValue (for the same element), you can try to use Aggregate:
var start = ageRangeList
.OrderBy(a => a.firstValue).Dump()
.First();
var result = ageRangeList
.OrderBy(a => a.firstValue)
.Aggregate(
(hasGap: false, s: start.secondValue),
(tuple, range) =>
{
if (tuple.hasGap)
{
return tuple;
}
else
{
var max = Math.Max(tuple.s, tuple.s+1); //hacky overflow protection
if (max < range.firstValue)
{
return (true, tuple.s);
}
else
{
return (false, Math.Max(tuple.s, range.secondValue));
}
}
})
.hasGap;
The downside of such approach is that it still will need to loop through all age ranges.
If you want to find first discontinuity and use that information elsewhere
public static IEnumerable<AgeRange> FindDiscontinuity(List<AgeRange> ageRangeList) {
foreach(var ageRange in ageRangeList.Zip(ageRangeList.Skip(1), (a, b) => new {Prev = a, Current = b})) {
if(ageRange.Prev.SecondValue != ageRange.Current.FirstValue) {
yield return ageRange.Prev;
yield return ageRange.Current;
break;
}
}
}
public static void Main()
{
var ageRange1 = new AgeRange(0, 2);
var ageRange2 = new AgeRange(4, 10);
var ageRange3 = new AgeRange(11, int.MaxValue);
var ageRangeList = new List<AgeRange>();
ageRangeList.Add(ageRange1);
ageRangeList.Add(ageRange2);
ageRangeList.Add(ageRange3);
var result = FindDiscontinuity(ageRangeList);
foreach(var ageRange in result) {
Console.WriteLine("{0}, {1}", ageRange.FirstValue, ageRange.SecondValue);
}
}
You can change the function so it can return boolean value instead of data.
Need suggestions on how to order the strings in order in a linq query.
Example of strings in db [1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 1.10 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.18 1.19 1.20 2.1a(i) 2.1a(ii) 2.1a(iii) 2.1a(iv) 2.1a(v) 2.1a(vi) 2.1a(vii) , ....]
In order to solve the issue for a job case.
I have written a linq query which suppose to order the jobs based on band level
var GetOrder =(from a in db.Details
join b in db.Information on a.InfromationId equals b.Id
where c.JobId == JobId
select new {a.Name, a.LastName, b.BandLevel}
//.OrderBy(b=>b.BandLevel)
.ToList();
I added the below query for it can order and sort for strings.
GetOrder.Sort((a, b) => a.BandLevel.CompareTo(b.BandLevel));
This query is suppose to sort the stings in order but it fails to set for some strings.
Instead it orders in this format using the above query.
1.1 , 1.10, 1.19 ,1.2, 2.1a(i) ,2.21(v)
It should be in this desired list .
1.1 ,1.2, 1.10, 1.19 , 2.1a(i) ,2.21(v)
Any suggestions on how to sort it in the suggested order for linq queries.
Well, p.s.w.g has a great answer, but since I worked on this a little I figured I would post mine as well.
My suggestion is to create a class that encapsulates the data from the string, which can only be instantiated from a static Parse method. This Parse method takes in a string and then parses it, setting the properties of the class as it does, and then returns the instance of the class.
The class also implements IComparable, so we can use the Sort and OrderBy methods on a list of these items.
I also used the same answer for parsing roman numerals that was used above (it's the first result when searching for "roman numeral comparer").
Here's the class:
public class BandLevelComponent : IComparable<BandLevelComponent>
{
public int Major { get; private set; }
public int Minor { get; private set; }
public string Revision { get; private set; }
public string RomanNumeral { get; private set; }
private static Dictionary<char, int> _romanMap = new Dictionary<char, int>
{
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000}
};
private BandLevelComponent()
{
}
public static BandLevelComponent Parse(string input)
{
if (string.IsNullOrWhiteSpace(input)) return null;
BandLevelComponent result = new BandLevelComponent();
int temp;
var parts = input.Split('.');
int.TryParse(parts[0], out temp);
result.Major = temp;
if (parts.Length > 1)
{
var minor = string.Concat(parts[1].TakeWhile(char.IsNumber));
int.TryParse(minor, out temp);
result.Minor = temp;
if (parts[1].Length > minor.Length)
{
var remaining = parts[1].Substring(minor.Length);
var openParen = remaining.IndexOf("(");
if (openParen > 0) result.Revision = remaining.Substring(0, openParen);
if (openParen > -1)
result.RomanNumeral = remaining
.Split(new[] {'(', ')'}, StringSplitOptions.RemoveEmptyEntries)
.Last();
}
}
return result;
}
public int CompareTo(BandLevelComponent other)
{
if (other == null) return 1;
if (Major != other.Major) return Major.CompareTo(other.Major);
if (Minor != other.Minor) return Minor.CompareTo(other.Minor);
if (Revision != other.Revision) return Revision.CompareTo(other.Revision);
return RomanNumeral != other.RomanNumeral
? RomanToInteger(RomanNumeral).CompareTo(RomanToInteger(other.RomanNumeral))
: 0;
}
public override string ToString()
{
var revision = Revision ?? "";
var roman = RomanNumeral == null ? "" : $"({RomanNumeral})";
return $"{Major}.{Minor}{revision}{roman}";
}
private static int RomanToInteger(string romanNumeral)
{
var roman = romanNumeral?.ToUpper();
var number = 0;
for (var i = 0; i < roman?.Length; i++)
{
if (i + 1 < roman.Length && _romanMap[roman[i]] < _romanMap[roman[i + 1]])
{
number -= _romanMap[roman[i]];
}
else
{
number += _romanMap[roman[i]];
}
}
return number;
}
}
And here's a sample usage:
private static void Main()
{
var dbStrings = new[]
{
"1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11",
"1.12", "1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.20", "2.1a(i)",
"2.1a(ii)", "2.1a(iii)", "2.1a(iv)", "2.1a(v)", "2.1a(vi)", "2.1a(vii)"
};
// Custom extension method for shuffling
dbStrings.Shuffle();
// Select each string into our custom class
var bandLevels = dbStrings.Select(BandLevelComponent.Parse).ToList();
Console.WriteLine("\nShuffled List");
Console.WriteLine(string.Join(", ", bandLevels));
// Sort the list
bandLevels.Sort();
Console.WriteLine("\nSorted List");
Console.WriteLine(string.Join(", ", bandLevels));
// Order the list descending (largest first)
bandLevels = bandLevels.OrderByDescending(b => b).ToList();
Console.WriteLine("\nOrderByDescending List");
Console.WriteLine(string.Join(", ", bandLevels));
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
Here's my stab at it. First split the string into various parts, e.g. 2.11a(ii) will become 2, 11, a, and ii. The first two parts can be parsed as regular integers. The second part is parsed as a integer with a=1, b=2 and so on. The third part is parsed as a Roman numeral (I used a modified version of the algorithm presented in this answer). You collect these parts as an array of integers (I call them indexes), and compare the array from one item to the next such that if the first index of each is equal, the items are equal, and so on until an index is unequal.
public static int CustomComparison(string x, string y)
{
var xIndexes = StringToIndexes(x);
var yIndexes = StringToIndexes(y);
for (int i = 0; i < 4; i++)
{
if (xIndexes[i] < yIndexes[i])
{
return -1;
}
if (xIndexes[i] > yIndexes[i])
{
return 1;
}
}
return 0;
}
private static int[] StringToIndexes(string input) {
var match = Regex.Match(input, #"^(\d+)\.(\d+)([a-z]+)?(?:\(([ivx]+)\))?$");
if (!match.Success)
{
return new[] { 0, 0, 0, 0 };
}
return new[] {
int.Parse(match.Groups[1].Value),
int.Parse(match.Groups[2].Value),
AlphabeticToInteger(match.Groups[3].Value),
RomanToInteger(match.Groups[4].Value),
};
}
private static int AlphabeticToInteger(string alpha)
{
return alpha.Aggregate(0, (n, c) => n * 26 + (int)(c - 'a' + 1));
}
private static Dictionary<char, int> RomanMap = new Dictionary<char, int>()
{
{'i', 1},
{'v', 5},
{'x', 10},
};
public static int RomanToInteger(string roman)
{
int number = 0;
for (int i = 0; i < roman.Length; i++)
{
if (i + 1 < roman.Length && RomanMap[roman[i]] < RomanMap[roman[i + 1]])
{
number -= RomanMap[roman[i]];
}
else
{
number += RomanMap[roman[i]];
}
}
return number;
}
When i have a list
IList<int> list = new List<int>();
list.Add(100);
list.Add(200);
list.Add(300);
list.Add(400);
list.Add(500);
What is the way to extract a pairs
Example : List elements {100,200,300,400,500}
Expected Pair : { {100,200} ,{200,300} ,{300,400} ,{400,500} }
The most elegant way with LINQ: list.Zip(list.Skip(1), Tuple.Create)
A real-life example: This extension method takes a collection of points (Vector2) and produces a collection of lines (PathSegment) needed to 'join the dots'.
static IEnumerable<PathSegment> JoinTheDots(this IEnumerable<Vector2> dots)
{
var segments = dots.Zip(dots.Skip(1), (a,b) => new PathSegment(a, b));
return segments;
}
This will give you an array of anonymous "pair" objects with A and B properties corresponding to the pair elements.
var pairs = list.Where( (e,i) => i < list.Count - 1 )
.Select( (e,i) => new { A = e, B = list[i+1] } );
You can use a for loop:
var pairs = new List<int[]>();
for(int i = 0; i < list.Length - 1; i++)
pairs.Add(new [] {list[i], list[i + 1]);
You can also use LINQ, but it's uglier:
var pairs = list.Take(list.Count - 1).Select((n, i) => new [] { n, list[i + 1] });
EDIT: You can even do it on a raw IEnumerable, but it's much uglier:
var count = list.Count();
var pairs = list
.SelectMany((n, i) => new [] { new { Index = i - 1, Value = n }, new { Index = i, Value = n } })
.Where(ivp => ivp.Index >= 0 && ivp.Index < count - 1) //We only want one copy of the first and last value
.GroupBy(ivp => ivp.Index, (i, ivps) => ivps.Select(ivp => ivp.Value));
More general would be:
public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> values, int count, Func<TSource[], TResult> pairCreator)
{
if (count < 1) throw new ArgumentOutOfRangeException("count");
if (values == null) throw new ArgumentNullException("values");
if (pairCreator == null) throw new ArgumentNullException("pairCreator");
int c = 0;
var data = new TSource[count];
foreach (var item in values)
{
if (c < count)
data[c++] = item;
if (c == count)
{
yield return pairCreator(data);
c = 0;
}
}
}
Following solution uses zip method. Zip originalList and originalList.Skip(1) so that one gets desired result.
var adjacents =
originalList.Zip(originalList.Skip(1),
(a,b) => new {N1 = a, N2 = b});
Using .Windowed() from MoreLINQ:
var source = new[] {100,200,300,400,500};
var result = source.Windowed(2).Select(x => Tuple.Create(x.First(),x.Last()));
Off the top of my head and completely untested:
public static T Pairwise<T>(this IEnumerable<T> list)
{
T last;
bool firstTime = true;
foreach(var item in list)
{
if(!firstTime)
return(Tuple.New(last, item));
else
firstTime = false;
last = item;
}
}
I have a list of lists that contain integers (this list can be any length and can contain any number of integers:
{{1,2}, {3,4}, {2,4}, {9,10}, {9,12,13,14}}
What I want to do next is combine the lists where any integer matches any integer from any other list, in this case:
result = {{1,2,3,4}, {9,10,12,13,14}}
I have tried many different approaches but am stuck for an elegant solution.
If you just mean "combine when there's an intersection", then maybe something like below, with output:
{1,2,3,4}
{9,10,12}
noting that it also passes the test in your edit, with output:
{1,2,3,4}
{9,10,12,13,14}
Code:
static class Program {
static void Main()
{
var sets = new SetCombiner<int> {
{1,2},{3,4},{2,4},{9,10},{9,12}
};
sets.Combine();
foreach (var set in sets)
{
// edited for unity: original implementation
// Console.WriteLine("{" +
// string.Join(",", set.OrderBy(x => x)) + "}");
StringBuilder sb = new StringBuilder();
foreach(int i in set.OrderBy(x => x)) {
if(sb.Length != 0) sb.Append(',');
sb.Append(i);
}
Console.WriteLine("{" + sb + "}");
}
}
}
class SetCombiner<T> : List<HashSet<T>>
{
public void Add(params T[] values)
{
Add(new HashSet<T>(values));
}
public void Combine()
{
int priorCount;
do
{
priorCount = this.Count;
for (int i = Count - 1; i >= 0; i--)
{
if (i >= Count) continue; // watch we haven't removed
int formed = i;
for (int j = formed - 1; j >= 0; j--)
{
if (this[formed].Any(this[j].Contains))
{ // an intersection exists; merge and remove
this[j].UnionWith(this[formed]);
this.RemoveAt(formed);
formed = j;
}
}
}
} while (priorCount != this.Count); // making progress
}
}
Build custom comparer:
public class CusComparer : IComparer<int[]>
{
public int Compare(int[] x, int[] y)
{
x = x.OrderBy(i => i).ToArray();
y = y.OrderBy(i => i).ToArray();
for (int i = 0; i < Math.Min(x.Length, y.Length); i++ )
{
if (x[i] < y[i]) return -1;
if (x[i] > y[i]) return 1;
}
if (x.Length < y.Length) return -1;
if (x.Length > y.Length) return 1;
return 0;
}
}
Then, order by custom comparer first:
List<int[]> input = new List<int[]>()
{
new[] { 3, 4 }, new[] { 1, 2 }, new[] { 2, 4 },
new[] { 9, 10 }, new[] { 9, 12 }
};
var orderedInput = input.OrderBy(x => x, new CusComparer()).ToList();
Use Intersect.Any() to check:
List<int[]> output = new List<int[]>();
int[] temp = orderedInput[0];
foreach (var arr in orderedInput.Skip(1))
{
if (temp.Intersect(arr).Any())
temp = temp.Union(arr).ToArray();
else
{
output.Add(temp);
temp = arr;
}
}
output.Add(temp);
Here's a simple, flexible solution using LINQ's Aggregate:
void Main()
{
var ints = new []{new []{1,2},new []{3,4},new []{2,4},new []{9,10},new []{9,12}};
var grouped = ints.Aggregate(new List<HashSet<int>>(), Step);
foreach(var bucket in grouped)
Console.WriteLine(String.Join(",", bucket.OrderBy(b => b)));
}
static List<HashSet<T>> Step<T>(List<HashSet<T>> all, IEnumerable<T> current)
{
var bucket = new HashSet<T>();
foreach (var c in current)
bucket.Add(c);
foreach (var i in all.Where(b => b.Overlaps(bucket)).ToArray())
{
bucket.UnionWith(i);
all.Remove(i);
}
all.Add(bucket);
return all;
}
We maintain a list of resulting sets (1). For each source set, remove resulting sets that intersect it (2), and add a new resulting set (3) that is the union of the removed sets and the source set (4):
class Program {
static IEnumerable<IEnumerable<T>> CombineSets<T>(
IEnumerable<IEnumerable<T>> sets,
IEqualityComparer<T> eq
) {
var result_sets = new LinkedList<HashSet<T>>(); // 1
foreach (var set in sets) {
var result_set = new HashSet<T>(eq); // 3
foreach (var element in set) {
result_set.Add(element); // 4
var node = result_sets.First;
while (node != null) {
var next = node.Next;
if (node.Value.Contains(element)) { // 2
result_set.UnionWith(node.Value); // 4
result_sets.Remove(node); // 2
}
node = next;
}
}
result_sets.AddLast(result_set); // 3
}
return result_sets;
}
static IEnumerable<IEnumerable<T>> CombineSets<T>(
IEnumerable<IEnumerable<T>> src
) {
return CombineSets(src, EqualityComparer<T>.Default);
}
static void Main(string[] args) {
var sets = new[] {
new[] { 1, 2 },
new[] { 3, 4 },
new[] { 2, 4 },
new[] { 9, 10 },
new[] { 9, 12, 13, 14 }
};
foreach (var result in CombineSets(sets))
Console.WriteLine(
"{{{0}}}",
string.Join(",", result.OrderBy(x => x))
);
}
}
This prints:
{1,2,3,4}
{9,10,12,13,14}
Ok i LINQed this up! Hope this is what you wanted... crazy one ;)
void Main()
{
var matches = new List<List<ComparissonItem>> { /*Your Items*/ };
var overall =
from match in matches
let matchesOne =
(from searchItem in matches
where searchItem.Any(item => match.Any(val => val.Matches(item) && !val.Equals(item)))
select searchItem)
where matchesOne.Any()
select
matchesOne.Union(new List<List<ComparissonItem>> { match })
.SelectMany(item => item);
var result = overall.Select(item => item.ToHashSet());
}
static class Extensions
{
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> enumerable)
{
return new HashSet<T>(enumerable);
}
}
class ComparissonItem
{
public int Value { get; set; }
public bool Matches(ComparissonItem item)
{
/* Your matching logic*/
}
public override bool Equals(object obj)
{
var other = obj as ComparissonItem;
return other == null ? false : this.Value == other.Value;
}
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
}
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.
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);
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);
}
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()
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");
}