Merge two reports to create a four column single report - c#

This is related to a GameBetting related Project.
We have Two simple Lists of following Class.
class Gameresults
{
int userid,
double amount
}
The following two reports are printed on paper.
A) Game Winners
uid amount
10 -
14 -
15 -
B) Game Losers
uid amount
11 -
12 -
13 -
16 -
Since the columns in such report leaves space across
the width of a Paper, we have to merge both the reports and
prepare the following report
Game Winners Game Losers
uid Amount uid Amount | uid Amount uid Amount
10 - 15 - 11 - 13 -
14 - 12 - 16 -
The above report has two columns for each report.
The Row length of WinnerReport is totalrecords/2 , therefore two records in first
column and 1 record in next column
So first the right column is filled, rest goes to the left column
Same for LosersReport
the Rowlength of Winner vs Losers reports is not important.
They however must be equal (+/- 1) with respect to record count in their
respective Lists
I made a Class to put all the records in one row, as we use printer for output of the report
class MergeRow
{
int uidWinCol1; // userid,amount of winner on column 1
double amtWinCol1;
int uidWinCol2; // userid.amount of winner on column 2
double amtWinCol2;
int uidLosCol1;
double amtLosCol1;
int uidLosCol2;
double amtLosCol2;
}
I need advice for the part on how to merge both lists, i presume such
a method is possible only in Linq, but any pointer or link will be helpful.
thank you

You can split the winnersList and losersList, each into 2 halves. Thus you would have 4 sublists.
Now you can use FirstOrDefault on each sublist to get an instance ofMergeRow. In case one of the sublists become empty before the others, use DefaultIfEmpty, with a placeholder item.
The code would look like:
var winnersList = new List<Gameresults>();
var losersList = new List<Gameresults>();
//Populate the winnersList and losersList
var winnersList1 = winnersList.Take(winnersList.Count/2).ToList();
var winnersList2 = winnersList;
var losersList1 = losersList.Take(losersList.Count/2).ToList();
var losersList2 = losersList;
var allLists = new List<List<Gameresults>> {winnersList1, winnersList2, losersList1, losersList2};
var mergeRows = new List<MergeRow>();
while (allLists.Any(l => l.Count > 0))
{
var resultsInOneRow = allLists.Select(l => l.DefaultIfEmpty(new Gameresults()).FirstOrDefault()).ToList();
mergeRows.Add(GetMergeRow(resultsInOneRow));
}
Your GetMergeRow() method would look like:
private MergeRow GetMergeRow(List<Gameresults> recordsToMerge)
{
var mergeRow = new MergeRow();
mergeRow.uidWinCol1 = recordsToMerge[0].userid;
mergeRow.amtWinCol1 = recordsToMerge[0].amount;
//... and so on
return mergeRow;
}

Related

The problem with List Initialization with capacity using Enumerable.Repeat [duplicate]

This question already has answers here:
Passing Objects By Reference or Value in C#
(9 answers)
Closed 3 years ago.
If we are trying to initialize a List with capacity and default value, just like how we initialize a vector in C++ with size and default value, say I'm creating a list of Dictionary to build a graph with weight, there are generally two ways of doing that:
1) Using for loop to build the list manually:
var graph= new List<Dictionary<int, int>>();
for (var i = 0; i < n; i++)
{
graph.Add(new Dictionary<int, int>());
}
2) Using Enumberable.Repeat() to initialize the list:
var graph= Enumerable.Repeat(new Dictionary<int, int>(), n).ToList();
Say now we have a list of node-pair(edge) with weight and want to build a graph on top of that, we would write:
foreach (var list in lists) // List = [[0, 1, 100],[1,2,100],[0,2,500]]
{
int a = list[0], b = list[1], weight= list[2];
if (!graph[a].ContainsKey(b))
graph[a].Add(b, weight);
}
While both approach should theoretically work, the result are quite different.
When I try to print the graph with following code:
for (var i = 0; i < graph.Count; i++)
{
var node = graph[i];
foreach (var pair in node)
{
Console.WriteLine($"{i} -> {pair.Key} with price {pair.Value}");
}
}
The result from 1) looks like:
// Correct output
0 -> 1 with price 100
0 -> 2 with price 500
1 -> 2 with price 100
But result from 2) looks like:
// Incorrect result
0 -> 1 with price 100
0 -> 2 with price 100
1 -> 1 with price 100
1 -> 2 with price 100
2 -> 1 with price 100
2 -> 2 with price 100
The behavior of Repeat method seems very odd in this case and on the Doc, there is no mention of such behavior.
My take on that behavior is the Repeat method create ONLY ONE value in the memory and point all list entry to the same memory location. In this case there is only one dictionary created in the memory instead of a new dictionary created for each list entry. And all operations on the dictionary actually happens at the same location. But this won't explain the weird output in the second result.
Any thought? Is it a bug in this Enumerable.Repeat() method or I'm doing it wrong? What is the better way to initialize a list with default value?
Enumerable.Repeat simply duplicates the passed value n times. You create one dictionary, pass it to Repeat and it produces a list of n references to that dictionary.
If you want to generate a sequence based on a function, there's the MoreLinq Generate extension.

Accessing and extracting Data Frame Results - R.NET

I run a R script from C# and get the results in a dataframe as :
var OUTPUT = engine.GetSymbol("detail.dt").AsDataFrame();
OUTPUT dataframe has let's say 3 columns as:
NAME Month Rate
Rob 1 100
Rob 2 150
Rob 3 500
Ned 1 200
Ned 2 500
Sansa 1 500
Sansa 2 1000
I can extract individual column values as :
var Name = OUTPUT[0].AsEnumerable().ToList();
var Month = OUTPUT[1].AsNumeric().ToList();
var Rate = OUTPUT[2].AsNumeric().ToList();
My question is instead of extracting column by column values, I basically want to extract Month and Rate if user asks for "Rob" or "Sansa".
How do I extract Month and Rate values for a givenName?
Is there a better and faster way?
I would loop through the data.frame. Name appears to be duplicated in your data, but here's how you can get one of them:
string userInput = "Rob";
int myMonth = 0;
int myRate = 0;
for (int i = 0; i < OUTPUT.RowCount; ++i)
{
if (OUTPUT[i, 0].ToString() == userInput) {
myMonth = Convert.ToInt32(OUTPUT[i, 1].ToString());
myRate = Convert.ToInt32(OUTPUT[i, 2].ToString());
break;
}
}
If you need all of them, get rid of the break statement, and pile them into a list or whatever data structure that suits you.

Packing item from set of available packs

Suppose there is an Item that a customer is ordering - in this case it turns out they are ordering 176 (totalNeeded) of this Item.
The database has 5 records associated with this item that this item can be stored in:
{5 pack, 8 pack, 10 pack, 25 pack, 50 pack}.
A rough way of packing this would be:
Sort the array from biggest to smallest.
While (totalPacked < totalNeeded) // 176
{
1. Maintain an <int, int> dictionary which contains Keys of pack id's,
and values of how many needed
2. Add the largest pack, which is not larger than the amount remaining to pack,
increment totalPacked by the pack size
3. If any remainder is left over after the above, add the smallest pack to reduce
waste
e.g., 4 needed, smallest size is 5, so add one 5; one extra item packed
}
Based on the above logic, the outcome would be:
You need: 3 x 50 packs, 1 x 25 pack, 1 x 5 pack
Total Items: 180
Excess = 4 items; 180 - 176
The above is not too difficult to code, I have it working locally. However, it is not truly the best way to pack this item. Note: "best" means, smallest amount of excess.
Thus ... we have an 8 pack available, we need 176. 176 / 8 = 22. Send the customer 22 x 8 packs, they will get exactly what they need. Again, this is even simpler than the pseudo-code I wrote ... see if the total needed is evenly divisible by any of the packs in the array - if so, "at the very least" we know that we can fall back on 22 x 8 packs being exact.
In the case that the number is not divisible by an array value, I am attempting to determine possible way that the array values can be combined to reach at least the number we need (176), and then score the different combinations by # of Packs needed total.
If anyone has some reading that can be done on this topic, or advice of any kind to get me started it would be greatly appreciated.
Thank you
This is a variant of the Subset Sum Problem (Optimization version)
While the problem is NP-Complete, there is a pretty efficient pseudo-polynomial time Dynamic Programming solution to it, by following the recursive formulas:
D(x,i) = false x<0
D(0,i) = true
D(x,0) = false x != 0
D(x,i) = D(x,i-1) OR D(x-arr[i],i
The Dynamic Programming Solution will build up a table, where an element D[x][i]==true iff you can use the first i kinds of packs to establish sum x.
Needless to say that D[x][n] == true iff there is a solution with all available packs that sums to x. (where n is the total number of packs you have).
To get the "closest higher number", you just need to create a table of size W+pack[0]-1 (pack[0] being the smallest available pack, W being the sum you are looking for), and choose the value that yields true which is closest to W.
If you wish to give different values to the different pack types, this becomes Knapsack Problem, which is very similar - but uses values instead a simple true/false.
Getting the actual "items" (packs) chosen after is done by going back the table and retracing your steps. This thread and this thread elaborate how to achieve it with more details.
If this example problem is truly representative of the actual problem you are solving, it is small enough to try every combination with brute force using recursion. For example, I found exactly 6,681 unique packings that are locally maximized, with a total of 205 that have exactly 176 total items. The (unique) solution with minimum number of packs is 6, and that is { 2-8, 1-10, 3-50 }. Total runtime for the algorithm was 8 ms.
public static List<int[]> GeneratePackings(int[] packSizes, int totalNeeded)
{
var packings = GeneratePackingsInternal(packSizes, 0, new int[packSizes.Length], totalNeeded);
return packings;
}
private static List<int[]> GeneratePackingsInternal(int[] packSizes, int packSizeIndex, int[] packCounts, int totalNeeded)
{
if (packSizeIndex >= packSizes.Length) return new List<int[]>();
var currentPackSize = packSizes[packSizeIndex];
var currentPacks = new List<int[]>();
if (packSizeIndex + 1 == packSizes.Length) {
var lastOptimal = totalNeeded / currentPackSize;
packCounts[packSizeIndex] = lastOptimal;
return new List<int[]> { packCounts };
}
for (var i = 0; i * currentPackSize <= totalNeeded; i++) {
packCounts[packSizeIndex] = i;
currentPacks.AddRange(GeneratePackingsInternal(packSizes, packSizeIndex + 1, (int[])packCounts.Clone(), totalNeeded - i * currentPackSize));
}
return currentPacks;
}
The algorithm is pretty straightforward
Loop through every combination of number of 5-packs.
Loop through every combination of number of 8-packs, from remaining amount after deducting specified number of 5-packs.
etc to 50-packs. For 50-pack counts, directly divide the remainder.
Collect all combinations together recursively (so it dynamically handles any set of pack sizes).
Finally, once all the combinations are found, it is pretty easy to find all packs with least waste and least number of packages:
var packSizes = new int[] { 5, 8, 10, 25, 50 };
var totalNeeded = 176;
var result = GeneratePackings(packSizes, totalNeeded);
Console.WriteLine(result.Count());
var maximal = result.Where (r => r.Zip(packSizes, (a, b) => a * b).Sum() == totalNeeded).ToList();
var min = maximal.Min(m => m.Sum());
var minPacks = maximal.Where (m => m.Sum() == min).ToList();
foreach (var m in minPacks) {
Console.WriteLine("{ " + string.Join(", ", m) + " }");
}
Here is a working example: https://ideone.com/zkCUYZ
This partial solution is specifically for your pack sizes of 5, 8, 10, 25, 50. And only for order sizes at least 40 large. There are a few gaps at smaller sizes that you'll have to fill another way (specifically at values like 6, 7, 22, 27 etc).
Clearly, the only way to get any number that isn't a multiple of 5 is to use the 8 packs.
Determine the number of 8-packs needed with modular arithmatic. Since the 8 % 5 == 3, each 8-pack will handle a different remainder of 5 in this cycle: 0, 2, 4, 1, 3. Something like
public static int GetNumberOf8Packs(int orderCount) {
int remainder = (orderCount % 5);
return ((remainder % 3) * 5 + remainder) / 3;
}
In your example of 176. 176 % 5 == 1 which means you'll need 2 8-packs.
Subtract the value of the 8-packs to get the number of multiples of 5 you need to fill. At this point you still need to deliver 176 - 16 == 160.
Fill all the 50-packs you can by integer dividing. Keep track of the leftovers.
Now just fit the 5, 10, 25 packs as needed. Obviously use the larger values first.
All together your code might look like this:
public static Order MakeOrder(int orderSize)
{
if (orderSize < 40)
{
throw new NotImplementedException("You'll have to write this part, since the modular arithmetic for 8-packs starts working at 40.");
}
var order = new Order();
order.num8 = GetNumberOf8Packs(orderSize);
int multipleOf5 = orderSize - (order.num8 * 8);
order.num50 = multipleOf5 / 50;
int remainderFrom50 = multipleOf5 % 50;
while (remainderFrom50 > 0)
{
if (remainderFrom50 >= 25)
{
order.num25++;
remainderFrom50 -= 25;
}
else if (remainderFrom50 >= 10)
{
order.num10++;
remainderFrom50 -= 10;
}
else if (remainderFrom50 >= 5)
{
order.num5++;
remainderFrom50 -= 5;
}
}
return order;
}
A DotNetFiddle

Count and total repeating occurrences of a unique values in DataGridView column

EDIT Question originally: Count and total repeating occurrences of a number in a specific array position - i.e. how many 0's in array[34].
Changed to Count and total repeating occurrences of a unique values in DataGridView column
This is because I feel I asked the wrong question and the new one gives a more accurate sense of what I was looking for
This is pretty much exactly what I wanted: http://www.codeproject.com/Tips/421557/Counting-Unique-Products-in-a-DataGridView-and-Dis
I obviously could have described the question much better.
I have a byte array populated with a random-access file, I would like to count the amount of times, let's say, 0 or 127 occurs in a certain position. At the moment I can count the amount of successful matches to several numbers but the problem is totalling it. I have tried this: (unsurprisingly does not work)
if (array[34] == 0)
{
Label_a.Text = "abc";
}
int res_a = Regex.Matches(array[34].ToString(), "0").Count;
If I create a MessageBox with res_a, it recognises if it's a 0 or not by displaying a set message. But I would like to count and total the amount of occurrences in the background where the data totalled is going to be populated into a chart. General layout of current code:
// FileStream
// BinaryReader
// While length of file is larger than 0
// FileStream.Seek
// Foreach loop for array
// GET COUNT AND TOTAL HERE!
For example, I import a file with 4 records with different status' where status code 127 is repeated 3 times and 0 is just once. I want to get THIS information - how many times it has occurred!
Another example, this file has 12 records where status code 127 is repeated 6 times and 0 is 6 too:-
Based on your comment to L.B., I understand that you want to count the number of occurences of each "status" value, so I would try something like this:
int[] vals = new int[10] { 1, 1, 2, 2, 3, 3, 3, 4, 4, 1 };
var res = vals.GroupBy(v => v).ToDictionary(g => g.Key, g => g.Count());
res now holds a Dictionary where each KeyValuePair has a Key representing a Status code, and a Value representing the number of time this Status code is in the array.
You can now test a Status code's number of appearance like this:
int Code1;
res.TryGetValue(1, out Code1); // testing for Status code 1
And so on.
Cheers
Couldn't you do something like this?
var countOf0s = array.Count(x => x == 0);

Get closest/next match in .NET Hashtable (or other structure)

I have a scenario at work where we have several different tables of data in a format similar to the following:
Table Name: HingeArms
Hght Part #1 Part #2
33 S-HG-088-00 S-HG-089-00
41 S-HG-084-00 S-HG-085-00
49 S-HG-033-00 S-HG-036-00
57 S-HG-034-00 S-HG-037-00
Where the first column (and possibly more) contains numeric data sorted ascending and represents a range to determine the proper record of data to get (e.g. height <= 33 then Part 1 = S-HG-088-00, height <= 41 then Part 1 = S-HG-084-00, etc.)
I need to lookup and select the nearest match given a specified value. For example, given a height = 34.25, I need to get second record in the set above:
41 S-HG-084-00 S-HG-085-00
These tables are currently stored in a VB.NET Hashtable "cache" of data loaded from a CSV file, where the key for the Hashtable is a composite of the table name and one or more columns from the table that represent the "key" for the record. For example, for the above table, the Hashtable Add for the first record would be:
ht.Add("HingeArms,33","S-HG-088-00,S-HG-089-00")
This seems less than optimal and I have some flexibility to change the structure if necessary (the cache contains data from other tables where direct lookup is possible... these "range" tables just got dumped in because it was "easy"). I was looking for a "Next" method on a Hashtable/Dictionary to give me the closest matching record in the range, but that's obviously not available on the stock classes in VB.NET.
Any ideas on a way to do what I'm looking for with a Hashtable or in a different structure? It needs to be performant as the lookup will get called often in different sections of code. Any thoughts would be greatly appreciated. Thanks.
A hashtable is not a good data structure for this, because items are scattered around the internal array according to their hash code, not their values.
Use a sorted array or List<T> and perform a binary search, e.g.
Setup:
var values = new List<HingeArm>
{
new HingeArm(33, "S-HG-088-00", "S-HG-089-00"),
new HingeArm(41, "S-HG-084-00", "S-HG-085-00"),
new HingeArm(49, "S-HG-033-00", "S-HG-036-00"),
new HingeArm(57, "S-HG-034-00", "S-HG-037-00"),
};
values.Sort((x, y) => x.Height.CompareTo(y.Height));
var keys = values.Select(x => x.Height).ToList();
Lookup:
var index = keys.BinarySearch(34.25);
if (index < 0)
{
index = ~index;
}
var result = values[index];
// result == { Height = 41, Part1 = "S-HG-084-00", Part2 = "S-HG-085-00" }
You can use a sorted .NET array in combination with Array.BinarySearch().
If you get a non negative value this is the index of exact match.
Otherwise, if result is negative use formula
int index = ~Array.BinarySearch(sortedArray, value) - 1
to get index of previous "nearest" match.
The meaning of nearest is defined by a comparer you use. It must be the same you used when sorting the array. See:
http://gmamaladze.wordpress.com/2011/07/22/back-to-the-roots-net-binary-search-and-the-meaning-of-the-negative-number-of-the-array-binarysearch-return-value/
How about LINQ-to-Objects (This is by no means meant to be a performant solution, btw.)
var ht = new Dictionary<string, string>();
ht.Add("HingeArms,33", "S-HG-088-00,S-HG-089-00");
decimal wantedHeight = 34.25m;
var foundIt =
ht.Select(x => new { Height = decimal.Parse(x.Key.Split(',')[1]), x.Key, x.Value }).Where(
x => x.Height < wantedHeight).OrderBy(x => x.Height).SingleOrDefault();
if (foundIt != null)
{
// Do Something with your item in foundIt
}

Categories

Resources