Split a collection by half in size of its contents

I have a collection of numbers (Collection), and it can be of any size and contain negative and positive numbers. I am trying to break it down based on some criteria. starting with the first number in the collection, I want to create a collection, while this number is above -180 and below 180. Any numbers above 180 will go into the new collection, or any numbers below -180 will be included in the new collection. If the numbers fall into acceptable parameters again, they will again enter the new collection. the problem is that collections should remain in order.

For instance.

Take a collection of 100:

  • the first 50 are from 180 to -180.
  • the next 20 are below -180
  • the next 20 are above 180
  • the last 10 range from 180 to -180

From the collection above, I should now have 4 separate collections in the same order as the original collection 1.

  • The first collection numbers in the original order between 180 and -180
  • second collection number in original order below -180
  • third collection numbers in original order above 180
  • fourth set of numbers in the original order between 180 and -180

I made an attempt that I have not, and this is an unpleasant mess in the statements. I don't know linq very well, but I think there might be a more elegant solution using this. Can someone help me here by showing me how to create a linq statement or suggestions on how to make my if statements work if this is the best way.

Collection<Tuple<Collection<double>, int>> collectionOfDataSets = new Collection<Tuple<Collection<double>, int>>(); Collection<double> newDataSet = new Collection<double>(); for (int i = 0; i < dataSet.Count; i++) { if (dataSet[i] < 180 && dataSet[i] > -180) { newDataSet.Add(dataSet[i]); } else { Tuple<Collection<double>, int> lastEntry = collectionOfDataSets.LastOrDefault(b => b.Item2 == i--); if (lastEntry != null){ lastEntry.Item1.Add(dataSet[i]); } double lastInLastCollection = collectionOfDataSets.ElementAtOrDefault(collectionOfDataSets.Count).Item1.Last(); if (newDataSet.Count > 0 && lastInLastCollection!= dataSet[i]){ collectionOfDataSets.Add(new Tuple<Collection<double>, int>(newDataSet, i)); } newDataSet = new Collection<double>(); } } 

Thanks in advance for any help.

+4
source share
6 answers

Your example is complex. First I will formulate and solve a simpler problem, then I use the same method to solve your original problem.


I want to split the list of numbers into adjacent groups of even and odd numbers. For example, given the list of 2,2,4,3,6,2 , I would divide it into three groups [2,2,4], [3], [6,2]

This can be done using the GroupAdjacentBy method .

 > var numbers = new List<int>{2,2,4,3,6,2}; > numbers.GroupAdjacentBy(x => x % 2) [[2,2,4], [3], [6,2]] 

To solve your problem, simply replace the even classification function above with your classification function:

 > var points = new List<int>{-180,180}; > var f = new Func<int,int>(x => points.BinarySearch(x)); > var numbers = new List<int>{6,-50,100,190,200,20}; > numbers.GroupAdjacentBy(f) [[6,-50,100], [190,200], [20]] 
+1
source

If you need updated collections as soon as the values ​​change, why don't you use properties? Sort of

 // your original collection public IList<double> OriginalValues; //= new List<double> { -1000, 5, 7 1000 }; public IList<double> BelowMinus180 { get { return OriginalValues.Where(x => x < -180).ToList().AsReadOnly(); } } public IList<double> BetweenMinus180And180 { get { return OriginalValues.Where(x => x >= -180 && x <= 180).ToList().AsReadOnly(); } } public IList<double> Above180 { get { return OriginalValues.Where(x => x > 180).ToList().AsReadOnly(); } } 
0
source
 public static List<List<T>> PartitionBy<T>(this IEnumerable<T> seq, Func<T, bool> predicate) { bool lastPass = true; return seq.Aggregate(new List<List<T>>(), (partitions, item) => { bool inc = predicate(item); if (inc == lastPass) { if (partitions.Count == 0) { partitions.Add(new List<T>()); } partitions.Last().Add(item); } else { partitions.Add(new List<T> { item }); } lastPass = inc; return partitions; }); } 

Then you can use:

 List<List<double>> segments = newDataSet.PartitionBy(d => d > -180 && d < 180); 
0
source

How about this possible solution using two passes. In the first pass, we find that the indices took place changes, and in the second - the actual partition. First, an auxiliary method for determining the category:

  protected int DetermineCategory(double number) { if (number < 180 && number > -180) return 0; else if (number < -180) return 1; else return 2; } 

And then the actual algorithm:

  List<int> indices = new List<int>(); int currentCategory = -1; for (int i = 0; i < numbers.Count; i++) { int newCat = DetermineCategory(numbers[i]); if (newCat != currentCategory) { indices.Add(i); currentCategory = newCat; } } List<List<double>> collections = new List<List<double>>(indices.Count); for (int i = 1; i < indices.Count; ++i) collections.Add(new List<double>( numbers.Skip(indices[i - 1]).Take(indices[i] - indices[i - 1]))); 
0
source

Here is a new answer based on the new information you provided. I hope this time I will be closer to what you need.

 public IEnumerable<IList<double>> GetCollectionOfCollections(IList<double> values, IList<double> boundries) { var ordered = values.OrderBy(x => x).ToList(); for (int i = 0; i < boundries.Count; i++) { var collection = ordered.Where(x => x < boundries[i]).ToList(); if (collection.Count > 0) { ordered = ordered.Except(collection).ToList(); yield return collection.ToList(); } } if (ordered.Count() > 0) { yield return ordered; } } 
0
source

One method with linq. Unverified, but should work

 var firstSet = dataSet.TakeWhile(x=>x>-180&&x<180); var totalCount = firstSet.Count(); var secondSet = dataSet.Skip(totalCount).TakeWhile(x=>x<-180); totalCount+=secondSet.Count(); var thirdSet = dataSet.Skip(totalCount).TakeWhile(x=>x>180); totalCount += thirdSet.Count(); var fourthSet = dataSet.Skip(totalCount); 
0
source

All Articles