Reputation: 43
I have two collections, A and B. Everything that goes into collection A should sum to 0 (these have unique identifiers that push them into Collection A) while Collection B doesn't have to sum to 0. For example:
item1 = 200;
item2 = -200;
item3 = 200
These don't sum to 0 but have the same unique id's and are now in Collection A. I want to group them and compare the first two items and if they sum to 0, I move to the next items in the group and if those items don't sum to 0, I want to move them to collection B.
This is what I have now:
var grp= colA.AsEnumerable()
.Where(a => a.Field<string>("unique_id") != null)
.GroupBy(b=> b["unique_id"])
.Where(c=> c.Count() > 1).ToList();
foreach(var d in grp)
{
var sum = d.AsEnumerable().Sum(e => e.Field<decimal>("amount"));
}
if(sum != 0){//compare rows in group}
This successfully groups the items that don't equal 0 but I'm stuck at how to compare item1 and item2 then item3 so that item3 can be moved to collection B and Collection A will then be summed to 0.
Upvotes: 1
Views: 485
Reputation: 30464
If you use Skip / Take to enumerate over your sequence, you'll start at the front of your sequence over and over again. This can be done smarter!
its a bit unclear what you want in the following sequence:
200, -200, 10, 11, -11, 12, -12, 13, -13
200 and -200 stay in collection A. But do you remove only 10, and let the pairs [11, -11], [12, -12], [13, -13] in A, or do your remove [10, 11], [-11, 12], [-12, 13], -13?
let's suppose you want the latter. You need to split your input sequence into pairs of two. For this, let's make a re-usable function.
Input: a sequence of items and a splitSize Output: a sequence of ICollections where every ICollection has splitsize (except the last)
So input: 0 1 2 3 4 5 6 7 8 9 and SplitSize 3 Output: [0 1 2] [3 4 5] [6 7 8] [9]
I create this as an extension method, so you can use it in a LINQ concatenation. See extension methods demystified
public static IEnumerable<IList<TSource>> Split<TSource>(this IEnumerable<TSource> source,
int splitSize)
{
// todo: check for non-null source and positive splitSize
var enumerator = source.GetEnumerator();
while (enumerator.MoveNext())
{
// still elements to process. Create and fill the next splitList
List<TSource> splitList = new List<TSource>(splitSize);
splitList.Add(enumerator.Current()
while (splitList.Count < splitSize && enumerator.MoveNext())
{
// still elements to add:
splitList.Add(enumerator.Current);
}
// if here: either no more elements, or splitList full.
yield return splitList;
}
}
With the help of this function we can split your input in pairs [0 1] [2 3] [4 5] and check whether they should be in the collection of zero-sum or non-zero-sum:
void CreateZeroSumCollections<TSource>(IEnumerable<TSource> source,
Func<TSource, int> keySelector,
out IList<TSource> zeroCollection,
out IList<TSource> nonzeroCollection)
{
var zeroCollection = new List<TSource>();
var nonzeroCollection = new List<TSource>();
var splitSource = source.Split(2);
foreach (var splitElement in splitSource)
{
// the splitElement has a length of 2 or less
// Check if the sum is zero
if (splitElement.Count == 2
&& keySelector(splitElement[0]) == -keySelector(splitElement[1])
{ // 2 elements, and sum is zero
zeroCollection.AddRange(splitElement);
}
else
{ // either only 1 element or non zero sum
nonzeroCollection.AddRange(splitElement);
}
}
}
For this your sequence has to be enumerated only once.
Upvotes: 0
Reputation: 1739
Would something like this help you?
grp.ForEach(g =>
{
int stepNumber = 0;
int step = 2;
var target = g.Skip(stepNumber * step).Take(step);
if (target.Sum(x => x.Field<decimal>("amount")) != 0)
{
foreach (var item in target.Select(x => x))
{
colA.Rows.Remove(item);
colB.Rows.Add(item);
}
}
stepNumber ++;
});
Upvotes: 1