I am implementing a new report system in my company. The transfer of data from ERP to the report builder software is handled with a temporary JSON array.
Within the report builder I can manipulate the data with C#. Currently I am working on a new report for our shipping labels. Sometimes (not always, pseudocode follows) I need to print multiple labels of the same kind.
I think the easiest way to achieve this would be by iterating through the array and duplicate the entries according to my logic.
I could put this code in the "DoBeforeYouPrint"-Void of my report then and should get the desired amount of labels.
The data looks something like this (simplified)
{
"CustomerID": "1337",
"ArticleName": "Strawberry",
"DeliveryWeek": "45",
"PackagingCount": "50"
}
Pseudocode:
x = PackagingCount
y = Number of extra rows (labels)
foreach (Entry in DataSource) {
if x < 48 then
{
y = Math.Ceiling(x / 12)
}
if else x >= 48 then
{
y = Math.Ceiling(x / 60)
}
if else x > 840 then
{
y = 14
}
for (i = 1 to y)
{
Duplicate(Entry);
}
}
I need help with a function that will do the duplication of the row and copy all information into an identical new row directly beneath the original row.
Is this possible at all with JSON arrays? I am not a real programmer, I work in a greenhouse.
Related
I need to optimize below code so it can execute faster, by means using more memory or parallel, currently it is taking 2 minutes to complete single record in Windows 10 64bit, 16GB RAM PC
data1 list array length = 1000
data2 list array length = 100000
data3 list array length = 100
for (int d1 = 0; d1 < data1.Count; d1++)
{
if (data1[d1].status == 'UNMATCHED')
{
for (int d2 = 0; d2 < data2.Count; d2++)
{
if (data2[d2].status == 'UNMATCHED')
{
vMatched = false;
for (int d3 = 0; d3 < data3.Count; d3++)
{
if (data3[d3].rule == "rule1")
{
if (data1[d1].value == data2[d2].value)
{
data1[d1].status = 'MATCHED';
data1[d2].status = 'MATCHED';
vMatched = true;
break;
}
}
else if (data3[d3].rule == "rule2")
{
...
}
else if (data3[d3].rule == "rule100")
{
...
}
}
if (vMatched)
break;
}
}
}
}
First of all, for any kind of performance oriented programming, avoid using strings, use more appropriate types, like enum or bools, instead. Another recommendation is to profile your code, so you know what parts actually take time.
In the given example there is only one rule presented, so the data3-loop could be eliminated by first checking if this rule exist and only then proceed with the matching.
This matching between items in data1 & data2 essentially pairs unmatched items with the same value. Whenever problems like this occur, the standard solution is some kind of search structure, like a dictionary, to get better than linear search time. For example
var data2Dictionary = data2.ToDictionary(d => Tuple.Create(d.value, d.status), d => d);
This should let you drastically decrease the time to find a item with a specific value and status. Keep in mind that the code above will throw in case multiple items share the same value & status, and that the dictionary key will not be updated if the item changes value or status.
You can avoid to start everytime the 2nd loop from 0. By keeping last index with "UNMATCHED" inside data2.
It should reduce the complexity.
In the worst case:
Now 1000 * 100000 * 100 iterations: 10000000000
New (1000+100000) * 100 iterations: 10100000
Whilst using Vision OCR in c# xamarin. I found that the API returns text that is supposed to be in 1 region, in different regions. This causes unexcpected behaviour and incorrect processing of the data.
To solve this, Im required to extract the Y coordinates from the boundingboxes on the lines, save the accompanying data.
Add both to a list.
Cross reference each list entry with all the others. When two Y coordinates fall within a deviation of 10 they need to be combined.
I added a transcript below of the structure of the code. I tried using tuples, dictionaries, structs etc. But couldn't find a solution for cross referencing the values for any of them.
Does anyone have a pointer in the right direction?
UPDATE; Im making good progress using a recursive binary search combined with a tuple comparer. Il post the code if/when it works.
class Playground
{
public void meh()
{
//these are the boundingboxes of different regions when the returned value from the OCR api is parsed
// int[] arr=new int[]{ Left X, Top Y, Width, Height};
int[] arrA =new int[] { 178, 1141, 393, 91 };//item 3 xenos
int[] arrB =new int[] { 171, 1296, 216, 53 };//totaal 3items
int[] arrC =new int[] { 1183, 1134, 105, 51};//item 3 prijs
int[] arrD =new int[] { 1192, 1287, 107, 52 };//totaal prijs
//the strings as will be made available within the lines, as words
string strA = "item 3";
string strB = "totaal:";
string strC = "2,99";
string strD = "8,97";
//make list to hold our fake linedata
List<int[]> ourLines = new List<int[]>();
ourLines.Add(arrA); ourLines.Add(arrB); ourLines.Add(arrC); ourLines.Add(arrD);
//following structure is observed
for(int region = 0; region < 3; region++){
//3 regions for each region process lines
foreach(int[] lineData in ourLines)
{
//get Y coordinates from boundingbox which is: lineData[1]
//keep int in memory and link* words in the corresponding line to it.
//put int and its words in array outside region loop.
//repeat this a couple of 100 times
for (int words = 0; words < 180; words++)
{
//do stuff with words
}
}
}
//here i need a list with the Y coordinates (for example 1141, 1296,1134, 1287)
//cross reference all Y coordinates with eachother
//when they fall withing a deviation of 10 with another
//then build string with combined text
//search in text for words resembing 'total' and check if there is an approperiate monetary value.
//the above would link the values of arrays A + C and B + D.
//which causes the corresponding results to be; 'item 3 2.99' and 'totaal 8.97'
//currently the arrays A+B are returned in region one and C+D in region two. This also varies from image to image.
//necessary because vision OCR api sometimes decides that lists of productname + price do belong in 2 different regions
//same for totals, and this causes incorrect responses when reading the data. (like thinking the amount of products == the price
//so if you bought 3 items the ocr will think the total price is 3$. instead of 8.97 (when each item is 2.99)
//note, values are monetary and culture independant. so this can mean the values can be in xx.xx or xx,xx
//* with link i mean either make a tuple/list/struct/keyvaluepair/dictionary/or preferably something more approperiate.
// this code will be executed on android and iOS devices.. something lightweight is preferred.
}
}
after searching for a proper solution for the given problem, I could not accept the solutions in the forum and also the solutions provided by Microsoft are not what I was looking for.
Here is an example of the problem:
(Link to image) MSChart pie diagram with overlapping labels
I tried to set the labels outside of the diagram, but first, the connection lines are kind of ugly and second, the outside labels use too much space.
Also the SmartLabels didn't solve the problem.
Edit:
Does anybody know how to set the labels radius? It seems like the center of each label is a fixed radius?!
Did anybody figure out how to fix the overlapping labels?
Prepare your data!
You have to set points in the Series, so it is easy to create a list at first, enter your data to that list, sort the list in descending or ascending order of the value you want to display and than resort that list to place the smallest value between the two biggest value.
For example:
100
70
50
30
10
5
(old order)
100
5
70
10
50
30
(new order)
Resulting diagram:
(Link to image) MSChart pie diagram without overlapping labels
Lets call every slice of the pie a category, with a value and a tooltip text.
So you need a class like this:
private class CatValue
{
public string CategoryName;
public decimal Value;
public string TooltipText;
}
Now put your data in the List object, for example like this:
List<CatValue> listOfPieSlices = new List<CatValue>();
// ....
// Add your data to the list object
// ....
// Sort the list in ascending order
listOfPieSlices.Sort((x, y) => x.Value.CompareTo(y.Value));
// Sort the list in descending order
// listOfPieSlices.Sort((x, y) => -x.Value.CompareTo(y.Value));
//
// Now sort the "listOfPieSlices" with the specialSort methode:
listOfPieSlices = specialSort<CatValue>(listOfPieSlices);
// Now create the Series of DataPoints
Series sortedPoints = new Series("PCDSsorted");
for (int i = 0; i < listOfPieSlices.Count; i++)
{
sortedPoints.Points.AddY(Math.Abs(listOfPieSlices[i].Value));
sortedPoints.Points[i].Label = listOfPieSlices[i].CategoryName + ": "+listOfPieSlices[i].Value.ToString();
sortedPoints.Points[i].Font = new Font("Microsoft Sans Serif", 12);
sortedPoints.Points[i].LabelToolTip = listOfPieSlices[i].Tooltip;
sortedPoints.Points[i].ToolTip = listOfPieSlices[i].Tooltip;
}
private List<T> specialSort<T>(List<T> objects)
{
if (objects == null) return null;
int count = objects.Count;
List<T> sortedList = new List<T>();
for (int i = 0; i <= count - 1; i += 2)
{
if(i == count - 1)
sortedList.Add(objects[i / 2]);
else
{
sortedList.Add(objects[i / 2]);
sortedList.Add(objects[count - 1 - (i / 2)]);
}
}
return sortedList;
}
Hope this works for someone out there as well as it did for me.
Happy coding
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;
}
I have a flat file that is pipe delimited and looks something like this as example
ColA|ColB|3*|Note1|Note2|Note3|2**|A1|A2|A3|B1|B2|B3
The first two columns are set and will always be there.
* denotes a count for how many repeating fields there will be following that count so Notes 1 2 3
** denotes a count for how many times a block of fields are repeated and there are always 3 fields in a block.
This is per row, so each row may have a different number of fields.
Hope that makes sense so far.
I'm trying to find the best way to parse this file, any suggestions would be great.
The goal at the end is to map all these fields into a few different files - data transformation. I'm actually doing all this within SSIS but figured the default components won't be good enough so need to write own code.
UPDATE I'm essentially trying to read this like a source file and do some lookups and string manipulation to some of the fields in between and spit out several different files like in any normal file to file transformation SSIS package.
Using the above example, I may want to create a new file that ends up looking like this
"ColA","HardcodedString","Note1CRLFNote2CRLF","ColB"
And then another file
Row1: "ColA","A1","A2","A3"
Row2: "ColA","B1","B2","B3"
So I guess I'm after some ideas on how to parse this as well as storing the data in either Stacks or Lists or?? to play with and spit out later.
One possibility would be to use a stack. First you split the line by the pipes.
var stack = new Stack<string>(line.Split('|'));
Then you pop the first two from the stack to get them out of the way.
stack.Pop();
stack.Pop();
Then you parse the next element: 3* . For that you pop the next 3 items on the stack. With 2** you pop the next 2 x 3 = 6 items from the stack, and so on. You can stop as soon as the stack is empty.
while (stack.Count > 0)
{
// Parse elements like 3*
}
Hope this is clear enough. I find this article very useful when it comes to String.Split().
Something similar to below should work (this is untested)
ColA|ColB|3*|Note1|Note2|Note3|2**|A1|A2|A3|B1|B2|B3
string[] columns = line.Split('|');
List<string> repeatingColumnNames = new List<string();
List<List<string>> repeatingFieldValues = new List<List<string>>();
if(columns.Length > 2)
{
int repeatingFieldCountIndex = columns[2];
int repeatingFieldStartIndex = repeatingFieldCountIndex + 1;
for(int i = 0; i < repeatingFieldCountIndex; i++)
{
repeatingColumnNames.Add(columns[repeatingFieldStartIndex + i]);
}
int repeatingFieldSetCountIndex = columns[2 + repeatingFieldCount + 1];
int repeatingFieldSetStartIndex = repeatingFieldSetCountIndex + 1;
for(int i = 0; i < repeatingFieldSetCount; i++)
{
string[] fieldSet = new string[repeatingFieldCount]();
for(int j = 0; j < repeatingFieldCountIndex; j++)
{
fieldSet[j] = columns[repeatingFieldSetStartIndex + j + (i * repeatingFieldSetCount))];
}
repeatingFieldValues.Add(new List<string>(fieldSet));
}
}
System.IO.File.ReadAllLines("File.txt").Select(line => line.Split(new[] {'|'}))