I get Strings like this from my database:
NaN#Nan#44.20216139610997#45.35340149990988#45.44329482112824#45.1593428796393#NaN#NaN
values = SQLvalues.Split('#'); //produces Array you can see in the picture
(String[] values)
Going on further with strings until it ends with about 10 "NaN" Strings again.
What I am doing now is that I sum up all the values from that one Array.
But there will be about 100 more Arrays after this one and I need to add up for example values[8] from this Array with the one at the same position from the next Array.
hope this visualizes better what I need to do
As I am still an apprentice I don´t have much knowledge on all of this.
I´ve been trying to come with a solution for several hours now but I won´t seem to get anything to work here.
Any help would be great!
My Code:
String[] values;
String returnString = "";
List<Double> valueList = new List<Double>();
DateTime time = (DateTime)skzAdapterText.MinTambourChangedTime();
DataTable profilData = skzAdapterText.LoadValuesText(time);
int rowCount = profilData.Rows.Count;
for (int i = 0; i < rowCount; i++)
{
String SQLvalues = (String)profilData.Rows[i][2];
values = SQLvalues.Split('#');
double summe = 0;
int counter = 0;
foreach (String tmpRow in values)
{
Double value;
if (double.TryParse(tmpRow, NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out value)
&& !double.IsNaN(value))
{
counter++;
summe = summe + value;
}
}
if (summe != 0 && counter != 0)
valueList.Add(summe / counter);
}
The basic sum can be reduced like so:
values = SQLvalues.Split('#');
double sum = values.Where(v => v != "NaN").Select(v => double.Parse(v)).Sum();
For a specific position, say index 8, within many rows:
//Read the data from DB
DataTable profilData = skzAdapterText.LoadValuesText(time);
//parse out the values
var rowValueArrays = // will be a List<double[]>
profilData.Rows.
Select(r => r[2].Split('#').Select(v => v == "NaN"?0.0:double.Parse(v)).ToArray()).
ToList();
// sum the entries at index 8
double sumAt8 = rowValueArrays.Select(r => r[8]).Sum();
You say you are an apprentice, and so the syntax here may be unfamiliar to you and seem difficult to understand. But I want to emphasize the power here. The combination of IEnumerable, lambda expressions, and linq operations reduced the original sample down to two lines of code, and solved the full problem in what is technically three lines (spread out a little for readability). If I wanted to sacrifice any sense of style or maintainability, we could do it in just one line of code.
In short, it is well worth your time to learn how to write code this way. With practice, reading and writing code this way can become easy and greatly increase your speed and capability as a programmer.
I also see attempts to compute an average. Continuing from the end of the previous code:
int countAt8 = rowValuesArrays.Count(r => r[8] != 0.0);
double average = sumAt8 / countAt8;
Finally, I need to point out delimited data like this in a column is an abuse of the database and very poor practice. Schemas like this are considered broken, and need to be fixed.
As you want to sum up the values at the same positions of the arrays, I assume that all these array have the same length. Then first declare the required arrays. You also must probably calculate the average for each array position, so you also need an array for the counter and the averages.
double[] average = null;
int rowCount = profilData.Rows.Count;
if (rowCount > 0) {
string[] values = ((string)profilData.Rows[0][2]).Split('#');
int n = values.Length;
double[] sum = new double[n];
double[] counter = new double[n];
for (int i = 0; i < rowCount; i++) {
values = ((string)profilData.Rows[i][2]).Split('#');
for (int j = 0; j < n; j++) {
if (double.TryParse(values[j], NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out double value) && !double.IsNaN(value)) {
counter[j]++;
sum[j] += value;
}
}
}
average = new double[n];
for (int i = 0; i < n; i++) {
if (counter[i] != 0) {
average[i] = sum[i] / counter[i];
}
}
}
You cannot calculate the average while summing up, since you must divide the total sum by the total count. Therefore, I added another loop calculating the averages at each array position after the summing phase.
I am planning to get an array of the averages of each column.
But my app crashes at sum[j] += int.Parse(csvArray[i,j]); due to a FormatException. I have tried using Convert.ToDouble and Double.Parse but it still throws that exception.
The increments in the for loop start at 1 because Row 0 and Column 0 of the CSV array are strings (names and timestamps). For the divisor or total count of the fields that have values per column, I only count the fields that are not BLANK, hence the IF statement. I think I need help at handling the exception.
Below is the my existing for the method of getting the averages.
public void getColumnAverages(string filePath)
{
int col = colCount(filePath);
int row = rowCount(filePath);
string[,] csvArray = csvToArray(filePath);
int[] count = new int[col];
int[] sum = new int[col];
double[] average = new double[col];
for (int i = 1; i < row; i++)
{
for (int j = 1; j < col; j++)
{
if (csvArray[i,j] != " ")
{
sum[j] += int.Parse(csvArray[i,j]);
count[j]++;
}
}
}
for (int i = 0; i < average.Length; i++)
{
average[i] = (sum[i] + 0.0) / count[i];
}
foreach(double d in average)
{
System.Diagnostics.Debug.Write(d);
}
}
}
I have uploaded the CSV file that I use when I test the prototype. It has BLANK values on some columns. Was my existing IF statement unable to handle that case?
There are also entries like this 1.324556-e09due to the number of decimals I think. I guess I have to trim it in the csvToArray(filePath) method or are there other efficient ways? Thanks a million!
So there are a few problems with your code. The main reason for your format exception is that after looking at your CSV file your numbers are surrounded by quotes. Now I can't see from your code exactly how you convert your CSV file to an array but I'm guessing that you don't clear these out - I didn't when I first ran with your CSV and experienced the exact same error.
I then ran into an error because some of the values in your CSV are decimal, so the datatype int can't be used. I'm assuming that you still want the averages of these columns so in my slightly revised verion of your method I change the arrays used to be of type double.
AS #musefan suggested, I have also changed the check for empty places to use the IsNullOrWhiteSpace method.
Finally when you output your results you receive a NaN for the first value in the averages column, this is because when you don't take into account that you never populate the first position of your arrays so as not to process the string values. I'm unsure how you'd best like to correct this behaviour as I'm not sure of the intended purpose - this might be okay - so I've not made any changes to this for the moment, pop a mention in the comments if you want help on how to sort this!
So here is the updated method:
public static void getColumnAverages(string filePath)
{
// Differs from the current implementation, reads a file in as text and
// splits by a defined delim into an array
var filePaths = #"C:\test.csv";
var csvArray = File.ReadLines(filePaths).Select(x => x.Split(',')).ToArray();
// Differs from the current implementation
var col = csvArray[0].Length;
var row = csvArray.Length;
// Update variables to use doubles
double[] count = new double[col];
double[] sum = new double[col];
double[] average = new double[col];
Console.WriteLine("Started");
for (int i = 1; i < row; i++)
{
for (int j = 1; j < col; j++)
{
// Remove the quotes from your array
var current = csvArray[i][j].Replace("\"", "");
// Added the Method IsNullOrWhiteSpace
if (!string.IsNullOrWhiteSpace(current))
{
// Parse as double not int to account for dec. values
sum[j] += double.Parse(current);
count[j]++;
}
}
}
for (int i = 0; i < average.Length; i++)
{
average[i] = (sum[i] + 0.0) / count[i];
}
foreach (double d in average)
{
System.Diagnostics.Debug.Write(d + "\n");
}
}
I'm trying to multiply the values of 2 listboxes together and make their product appear at another list box I'm getting the results I need but the problem is when I rerun the loop using a command button the listbox removes the next instance of the first value calculated by ppc[i] * qty[i] but when I try to remove the the listBox4.Items.Remove(ppc[i] * qty[i]) it reprints the whole array again from first element to last element
string myString = textBox1.Text.ToString();
int index = listBox6.FindString(myString, -1);
int[] qty = new int[99];
int[] ppc = new int[99];
int[] gt1 = new int[99];
listBox3.Items.Add(listBox5.Items[index]);
listBox1.Items.Add(textBox2.Text.ToString());
if (index != -1)
{
listBox6.SetSelected(index, true);
listBox2.Items.Add(textBox1.Text); //name
}
listBox3.Items.Add(listBox5.Items[index]);
listBox3.Items.Remove(listBox5.Items[index]);
for (int i = 0; i != listBox2.Items.Count ; i++)
{
ppc[i] = Convert.ToInt32(listBox3.Items[i]);
qty[i] = Convert.ToInt32(listBox1.Items[i]);
listBox4.Items.Remove(ppc[i] * qty[i]);
listBox4.Items.Add((ppc[i] * qty[i]));
}
My understanding is that this loop works once, and then when it is re-run it is out of order. Are you making sure to clear listbox4 each time this loop is executed? Also since listBox2 isn't used, it is probably better not to use it for your loop bounds.
if(listBox1.Items.Count == listBox3.Items.Count)
{
int rowCount = listBox1.Items.Count;
listBox4.Items.Clear();
for (int i=0; i < rowCount; i++)
{
ppc[i] = Convert.ToInt32(listBox3.Items[i]);
qty[i] = Convert.ToInt32(listBox1.Items[i]);
listBox4.Items.Insert(i , (ppc[i] * qty[i]));
}
}
I have a comma delimited text file that contains 20 digits separated by commas. These numbers represent earned points and possible points for ten different assignments. We're to use these to calculate a final score for the course.
Normally, I'd iterate through the numbers, creating two sums, divide and be done with it. However, our assignment dictates that we load the list of numbers into two arrays.
so this:
10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85
becomes this:
int[10] earned = {10,20,30,40,45,50,20,45,85};
int[10] possible = {10,20,35,50,50,50,20,90,85};
Right now, I'm using
for (x=0;x<10;x++)
{
earned[x] = scores[x*2]
poss [x] = scores[(x*2)+1]
}
which gives me the results I want, but seems excessively clunky.
Is there a better way?
The following should split each alternating item the list into the other two lists.
int[20] scores = {10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85};
int[10] earned;
int[10] possible;
int a = 0;
for(int x=0; x<10; x++)
{
earned[x] = scores[a++];
possible[x] = scores[a++];
}
You can use LINQ here:
var arrays = csv.Split(',')
.Select((v, index) => new {Value = int.Parse(v), Index = index})
.GroupBy(g => g.Index % 2,
g => g.Value,
(key, values) => values.ToArray())
.ToList();
and then
var earned = arrays[0];
var possible = arrays[1];
Get rid of the "magic" multiplications and illegible array index computations.
var earned = new List<int>();
var possible = new List<int>();
for (x=0; x<scores.Length; x += 2)
{
earned.Add(scores[x + 0]);
possible.Add(scores[x + 1]);
}
This has very little that would need a text comment. This is the gold standard for self-documenting code.
I initially thought the question was a C question because of all the incomprehensible indexing. It looked like pointer magic. It was too clever.
In my codebases I usually have an AsChunked extension available that splits a list into chunks of the given size.
var earned = new List<int>();
var possible = new List<int>();
foreach (var pair in scores.AsChunked(2)) {
earned.Add(pair[0]);
possible.Add(pair[1]);
}
Now the meaning of the code is apparent. The magic is gone.
Even shorter:
var pairs = scores.AsChunked(2);
var earned = pairs.Select(x => x[0]).ToArray();
var possible = pairs.Select(x => x[1]).ToArray();
I suppose you could do it like this:
int[] earned = new int[10];
int[] possible = new int[10];
int resultIndex = 0;
for (int i = 0; i < scores.Count; i = i + 2)
{
earned[resultIndex] = scores[i];
possible[resultIndex] = scores[i + 1];
resultIndex++;
}
You would have to be sure that an equal number of values are stored in scores.
I would leave your code as is. You are technically expressing very directly what your intent is, every 2nd element goes into each array.
The only way to improve that solution is to comment why you are multiplying. But I would expect someone to quickly recognize the trick, or easily reproduce what it is doing. Here is an excessive example of how to comment it. I wouldn't recommend using this directly.
for (x=0;x<10;x++)
{
//scores contains the elements inline one after the other
earned[x] = scores[x*2] //Get the even elements into earned
poss [x] = scores[(x*2)+1] //And the odd into poss
}
However if you really don't like the multiplication, you can track the scores index separately.
int i = 0;
for (int x = 0; x < 10; x++)
{
earned[x] = scores[i++];
poss [x] = scores[i++];
}
But I would probably prefer your version since it does not depend on the order of the operations.
var res = grades.Select((x, i) => new {x,i}).ToLookup(y=>y.i%2, y=>y.x)
int[] earned = res[0].ToArray();
int[] possible = res[1].ToArray();
This will group all grades into two buckets based on index, then you can just do ToArray if you need result in array form.
here is an example of my comment so you do not need to change the code regardless of the list size:
ArrayList Test = new ArrayList { "10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85" };
int[] earned = new int[Test.Count / 2];
int[] Score = new int[Test.Count / 2];
int Counter = 1; // start at one so earned is the first array entered in to
foreach (string TestRow in Test)
{
if (Counter % 2 != 0) // is the counter even
{
int nextNumber = 0;
for (int i = 0; i < Score.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(Score[i])))
{
nextNumber = i;
break;
}
}
Score[nextNumber] = Convert.ToInt32(TestRow);
}
else
{
int nextNumber = 0;
for (int i = 0; i < earned.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(earned[i])))
{
nextNumber = i;
break;
}
}
earned[nextNumber] = Convert.ToInt32(TestRow);
}
Counter++
}
I'm taking on the task of designing a game in unity. I'm not an expert at programming, but I have a fare share of knowledge in Ansi C. So forgive me if my code is C# like.
The method works just fine but for some strange reason the Debug.Log(string.Format("",) displays:
pop_class index value 0 value = 0,
pop_class index value 1 value = 1,
It displays the i variable and pop_class[i] if i simply type debug.log with the same information outside of method everything displays correctly. Any idea why this is the case? The program is complex enough i don't want to have to wrestle with log files while i'm debugging.
public int pop_generation() //debugger is currently not displaying vars correctly
{
int pop_increase = 1000000;
int pop_counter = 0;//counts populations assigned by the 1000s
int high_class_per = 10;
int mid_class_per = 50;
int low_class_per = 40;
int lpoptotal = (faction_pop/100) * low_class_per;
int lpop_count = 0;
int mpoptotal = (faction_pop/100) * mid_class_per;
int hpoptotal = (faction_pop/100) * high_class_per;
//calucates certain percentage of low class
int mid_class_pop = (faction_pop/100) * mid_class_per;
int high_class_pop = (faction_pop/100) * high_class_per;
System.Random random_now = new System.Random();
for(int i = 0; i < replimit_check && pop_counter < faction_pop;i++)
{
//keeps total of low class rep population by 1,000s
if(lpop_count > lpoptotal)
{
Debug.Log("Low class generation complete");
pop_class[i] = 0;
display_high_class();
i--;//brings incrementer back once
break;
}
pop_class[i] = random_now.Next(20);
pop_counter = pop_increase + pop_counter;
lpop_count = pop_increase + lpop_count;
if(debug_on)
Debug.Log(string.Format("Pop_class index value {0} value = {0} ",i,pop_class[i])); //debug.log is bugged
}
return 0;
}
Change this line:
Debug.Log(string.Format("Pop_class index value {0} value = {0} ",i,pop_class[i])); //debug.log is bugged
to this:
Debug.Log(string.Format("Pop_class index value {0} value = {1} ",i,pop_class[i])); //debug.log is bugged
You're printing out the first parameter twice, so pop_class[i] is never being printed. You need to change {0} to {1}.
The String.Format() function accepts an array of objects as its second parameter (at least in this case). This allows you to specify any number of arguments to the function. {0} tells Format() to output the second parameter to the function, {1} the third, etc. The first parameter is the format string.