I have 3 arrays. Two are arrays of strings and one is of date/time. I pulled all 3 from user input. Each array is always going to have the same exact amount of entries, so what I want to do is be able to loop through all 3 at once to make a string.
I was trying:
List<string> results = new List<string>();
// select
foreach (string line in array1)
{
foreach (string lines in array2)
{
foreach (DateTime date in datearray1)
{
results.Add("select * from table1 d, table2 c where d.specheader = c.specheader and c.true_false = true and d.number = " + lines.ToString() + " and d.date = '" + date.ToShortDateString() + "' and d.specnum like '%" + line.ToString() + "';");
}
}
}
results.ToArray();
foreach (string line in results)
{
MessageBox.Show(line);
}
The user types in information into 3 boxes and I'm just trying to concatenate sql statements based on the input. However when I tried doing it this way it looped through 6 times when I had only 2 entries. Is there a way to concatenate a string using all 3 arrays at the same time (so like entry 1 of array 1, entry 1 of array 2, entry 1 of array 3 - Then move on to creating the next string, entry 2 of array 1, entry 2 of array 2, entry 2 of array 3, etc.)
Any input would be appreciated. Thank you!
As the first commenter said (Yuck) don't use concatenation of strings into your SQL like that. You will want to setup an SQL Command and then pass in parameters.
That is however beside the point as you are asking about rolling together data from multiple arrays into 1 string.
Iterate through one of the arrays, If they all have the same count you will neatly get the data in one.
for(int i = 0; i < array1.Length; i++)
{
results.Add(string.format("Hello you! {0} , {1}, {2}", array1[i], array2[i], datearray[i])
}
This will get your desired result but your code is open to vulnerabilities as it stands. You need to change your approach.
Because your loops are nested, you're getting every value of array2 combined with every value in array1 (and similarly with datearray1. That's why you get too many results.
Your loops would work as intended like this (I've used similar local variables to avoid retyping the results.Add line, and to make clear how the code differs from yours):
for (int i = 0; i < array1.Length; i++)
{
string line = array1[i];
string lines = array2[i];
DateTime date = datearray1[i];
results.Add("select * from table1 d, table2 c where d.specheader = c.specheader and c.true_false = true and d.number = " + lines.ToString() + " and d.date = '" + date.ToShortDateString() + "' and d.specnum like '%" + line.ToString() + "';");
}
As a side-note: building a database query in this manner is inefficent and very insecure (try reading up on "Sql Injection" to understand why). You would see better results if you used a stored procedure instead.
if number of entries are going to be same for all you can simple do a for loop
for (int 1 = 0; i < datearray1.length; i++)
{
results.Add("select * from table1 d, table2 c
where d.specheader = c.specheader and c.true_false = true
and d.number = " + array2[i].ToString() + "
and d.date = '" + datearray1[i].ToShortDateString() + "'
and d.specnum like '%" + array1[i].ToString() + "';");
}
Related
I've made a function that builds a query to insert into mysql. The upload is blazing fast but for inserting longer values the building takes somewhat longer. Is there A way to speed up a function like this? Because I know that a loop in a loop takes a lot of time for higher amounts of data.
foreach (string[] st in dataToUpload)
{
buildQuery += " ('";
for (int i = 0; i < st.Length; i++)
{
buildQuery += st[i];
if (i < st.Length - 1)
buildQuery += "','";
}
buildQuery += "')";
if (st != dataToUpload[dataToUpload.Count - 1])
buildQuery += ",";
}
This is the query I would like to build for example;
string test = INSERT INTO test (test, test1, test2, test3) values
test = test + " " + buildquery;
so test will be
INSERT INTO test (test, test1, test2, test3)
values ("testvalue1", "testvalue2" , "testvalue3" , "testvalue4"),
("testvalue1", "testvalue2" , "testvalue3" , "testvalue4"),
I can work with INNODB and MYISAM and it's working on a centos server with a 6700k processor with 32gb ram.
So the main question is: How can I make the building of the query faster?
I would recommend to use a StringBuilder which gets Initialized to the right size right from the beginning. This reduces reallocation of memory on every string append.
Assuming that dataToUpload is a List you can try this:
// String Builder Initialization
// Size is calculated by getting the length of all strings and add 3 for each (',').
// Additionally there are 6 chars for " ('" and "')," per array
StringBuilder build = new StringBuilder(dataToUpload.Sum(data => data.Sum(s => s.Length) + data.Length * 3) + 6);
foreach (string[] st in dataToUpload)
{
build.Append(" ('" + string.Join<string>("','", st) + "'),");
}
buildQuery = build.ToString().TrimEnd(',');
Seems that your buildQuery is a String. Try StringBuilder instead. It's probably the best way to do string concatenation.
Feels like need more information here, but I suppose your are building an insert statement like this:
INSERT INTO MyTable ( Column1, Column2 ) VALUES
( Value1, Value2 ), ( Value1, Value2 )
So, perhaps the best way to not do a for inside a foreach is replacing the string[] in the foreach, for a string with the correct values. Something like this:
var count = 0;
foreach (string st in dataToUpload)
{
buildQuery += " ('" + st + "'") "
if (count++!=0 )
buildQuery += ","
}
Maybe this:
var count = dataToUpload.Count;
var i = 0;
foreach (string[] st in dataToUpload)
{
buildQuery += " ('" + string.Join(",", st) + "')";
if (i++ < count - 1)
buildQuery += ",";
}
Instead of comparing the st with the contents of the dataToUpload, use an index variable to speed it up.
And string.Join is a good way to concatenate strings.
So I'm using a C# code to separate lines of strings and then further separate them using a comma.
I'm using this two to separate:
dataString = hs_get.text.Split('\n');
stringPairs = new string[dataString.Length][];
for (int o = 0; o < dataString.Length - 1; o++)
{
Debug.Log (o);
stringPairs[o] = dataString[o].Split(',');
//Debug.Log (dataString[o][0] + " --- " + dataString[o][2]);
}
This is the PHP code that feeds this string:
php
// Send variables for the MySQL database class.
$database = mysql_connect('X', 'X', 'X') or die('Could not connect: ' . mysql_error());
mysql_select_db('openfire_scores') or die('Could not select database');
$query = "SELECT * FROM `scores` ORDER by `score` DESC LIMIT 5";
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
$num_results = mysql_num_rows($result);
for($i = 0; $i < $num_results; $i++)
{
$row = mysql_fetch_array($result); // Automatically advances one row after fetching
echo $row['id'] . "," . $row['name'] . "," . $row['score'] . "\n";
}
>
And this is the table thats being used:
id | name | score |
1 | Player 1 | 5 |
2 | Player 2 | 5 |
3 | Player 3 | 7 |
But instead of separating the words between the comma's, it separates every single character INCLUDING the comma and puts that into the array.
What am I doing wrong?
Edit
I should mention I built off of this post: http://answers.unity3d.com/questions/16225/how-can-i-read-data-from-a-text-file-putting-a-lar.html
Edit 2
So today I had a breakthrough. This is the code I use now. I am still not sure why the previous code didn't work, but this one does.
dataString = hs_get.text.Split('\n');
stringPairs = new string[dataString.Length][];
for (int o = 0; o < dataString.Length; o++)
{
string[] trial = dataString[o].Split(',');
stringPairs[o] = trial;
}
Thanks for the all the help guys!
Your code appears to work but skips the last row, you should drop the -1 unless you intend to skip the last row.
Here is an example:
string text = "1,Player 1,5\n2,Player 2,5\n3,Player 3,7";
string [] dataString = text.Split('\n');
string [][] stringPairs = new string[dataString.Length][];
for (int o = 0; o < dataString.Length; o++)
{
//Debug.Log(o);
stringPairs[o] = dataString[o].Split(',');
//Debug.Log (dataString[o][0] + " --- " + dataString[o][2]);
}
the array looks as follows:
stringPair[0]
[0]1
[1]Player 1
[2]5
stringPair[1]
[0]2
[1]Player 2
[2]5
stringPair[2]
[0]3
[1]Player 3
[2]7
You don't have to mirror the way the PHP code does it.
You could write something like this:
private string[][] Work(string hs_get_text) {
var lines = hs_get_text.Split('\n');
int linesLength = lines.Length;
var pairs = new List<string[]>(linesLength);
foreach (var line in lines) {
pairs.Add(line.Split(','));
}
return pairs.ToArray();
}
The List class is very powerful. If you are in .Net, you should use it to save some headaches.
Perhaps I am missing something, but this is working fine for me, perhaps a better alternative. I tried recreating the sample from the unity answer.
const string hs_get_text = "apple,ball\ncar,dog\negg,fish\ngoat,hat";
var dataString = hs_get_text.Split('\n');
var list = new List<string[]>(dataString.Length - 1);
foreach (string s in dataString)
{
Debug.WriteLine(s);
list.Add(s.Split(','));
}
And if you needed the list of string to be an array, you can just call .ToArray() on the list.
Originally, I was using a short C# program I wrote to average some numbers. But now I want to do more extensive analysis so I converted my C# code to R. However, I really don't think that I am doing it the proper way in R or taking advantage of the language. I wrote the R in the exact same way I did the C#.
I have a CSV with two columns. The first column identifies the row's type (one of three values: C, E, or P) and the second column has a number. I want to average the numbers grouped on the type (C, E, or P).
My question is, what is the idiomatic way of doing this in R?
C# code:
string path = "data.csv";
string[] lines = File.ReadAllLines(path);
int cntC = 0; int cntE = 0; int cntP = 0; //counts
double totC = 0; double totE = 0; double totP = 0; //totals
foreach (string line in lines)
{
String[] cells = line.Split(',');
if (cells[1] == "NA") continue; //skip missing data
if (cells[0] == "C")
{
totC += Convert.ToDouble(cells[1]);
cntC++;
}
else if (cells[0] == "E")
{
totE += Convert.ToDouble(cells[1]);
cntE++;
}
else if (cells[0] == "P")
{
totP += Convert.ToDouble(cells[1]);
cntP++;
}
}
Console.WriteLine("C found " + cntC + " times with a total of " + totC + " and an average of " + totC / cntC);
Console.WriteLine("E found " + cntE + " times with a total of " + totE + " and an average of " + totE / cntE);
Console.WriteLine("P found " + cntP + " times with a total of " + totP + " and an average of " + totP / cntP);
R code:
dat = read.csv("data.csv", header = TRUE)
cntC = 0; cntE = 0; cntP = 0 # counts
totC = 0; totE = 0; totP = 0 # totals
for(i in 1:nrow(dat))
{
if(is.na(dat[i,2])) # missing data
next
if(dat[i,1] == "C"){
totC = totC + dat[i,2]
cntC = cntC + 1
}
if(dat[i,1] == "E"){
totE = totE + dat[i,2]
cntE = cntE + 1
}
if(dat[i,1] == "P"){
totP = totP + dat[i,2]
cntP = cntP + 1
}
}
sprintf("C found %d times with a total of %f and an average of %f", cntC, totC, (totC / cntC))
sprintf("E found %d times with a total of %f and an average of %f", cntE, totE, (totE / cntE))
sprintf("P found %d times with a total of %f and an average of %f", cntP, totP, (totP / cntP))
I would use the data.table package since it has group by functionality built in.
library(data.table)
dat <- data.table(dat)
dat[, mean(COL_NAME_TO_TAKE_MEAN_OF), by=COL_NAME_TO_GROUP_BY]
# no quotes for the column names
If you would like to take the mean (or perform other function) on multiple columns, still by group, use:
dat[, lapply(.SD, mean), by=COL_NAME_TO_GROUP_BY]
Alternatively, if you want to use Base R, you could use something like
by(dat, dat[, 1], lapply, mean)
# to convert the results to a data.frame, use
do.call(rbind, by(dat, dat[, 1], lapply, mean) )
I would do something like this :
dat = dat[complete.cases(dat),] ## The R way to remove missing data
dat[,2] <- as.numeric(dat[,2]) ## convert to numeric as you do in c#
by(dat[,2],dat[,1],mean) ## compute the mean by group
Of course to aggregate your result in a data.frame you can use the the classic , But I don't think is necessary here since it a list of 3 variables:
do.call(rbind,result)
EDIT1
Another option here is to use the elegant ave :
ave(dat[,2],dat[,1])
But the result is different here. In the sense you will get a vector of the same length as your original data.
EDIT2 To include more results you can elaborate your anonymous function:
by(dat[,2],dat[,1],function(x) c(min(x),max(x),mean(x),sd(x)))
Or returns data.frame more suitable to rbind call and with columns names:
by(dat[,2],dat[,1],function(x)
data.frame(min=min(x),max=max(x),mean=mean(x),sd=sd(x)))
Or use the elegant built-in function ( you can define your's also) summary:
by(dat[,2],dat[,1],summary)
One way:
library(plyr)
ddply(dat, .(columnOneName), summarize, Average = mean(columnTwoName))
Say that I have a set of numbers:
Group1 = 10, Group2 = 15, Group3 = 20, Group4 = 30
I want to output the summation of all subsets of numbers
10 + 15 = 25
10 + 15 + 20 = 45
10 + 15 + 20 + 30 = 75
15 + 20 = 35
15 + 20 + 30 = 65
20 + 30 = 50
10 + 20 = 30
10 + 30 = 40
10 + 20 + 30 = 60
... (assumed the rest is typed out)
Each of these groups will have a name, so I would want to print out the names used in the calculation before the result:
Group1 + Group2 = 25
How to do such a thing?
EDIT: to JacobM who edited tags, this is NOT homework and would appreciate an ask before you start editing it as such. I am actually at a customer site who is trying to balance a set of numbers, and the result is coming up incorrectly. My thought was to identify which group of numbers is equal to the delta between the 2 sets, and that would identify the problem directly.
Note: this would be float values, not integers.
EDIT2: added arbitrary so that it is understood that I can not just type this out once with a bunch of string.format's .. I could easily use excel at that point.
My thought was to identify which group of numbers is equal to the delta between the 2 sets, and that would identify the problem directly.
The problem "given an integer s, and a set of integers, does any non-empty subset of the set sum to s?" is known as the "subset sum problem". It is extremely well studied, and it is NP-Complete. (See this link for a related problem.)
That is to say it is amongst the hardest problems to solve in a reasonable amount of time. It is widely believed (though at present not proved) that no polynomial-time algorithm can possibly exist for this problem. The best you can do is something like O(2^n) for a set containing n elements.
(I note that your problem is in floats, not integers. It doesn't really matter, as long as you correctly handle the comparison of the calculated sum to the target sum to handle any rounding error that might have accrued in doing the sum.)
For a small number of elements -- you say you have only 15 or so in the set -- your best bet is to just try them all exhaustively. Here's how you do that.
The trick is to realize that there is one subset for each integer from 0 to 2^n. If you look at those numbers in binary:
0000
0001
0010
0011
...
each one corresponds to a subset. The first has no members. The second has just group 1. The third has just group 2. The fourth has group 1 and group 2. And so on.
The pseudocode is easy enough:
for each integer i from 1 to 2^n
{
sum = 0;
for each integer b from 1 to n
{
if the bth bit of i is on then sum = sum + group(b)
}
if sum == target then print out i in binary and quit
}
quit with no solution
Obviously this is O(n 2^n). If you can find an algorithm that always does better than O(c^n), or prove that you cannot find such an algorithm then you'll be famous forever.
The Wikipedia article has a better algorithm that gives an answer much faster most but not all of the time. I would go with the naive algorithm first since it will only take you a few minutes to code up; if it is unacceptably slow then go for the faster, more complex algorithm.
This matches every possible combination...
static void Main(string[] args)
{
Dictionary<string, float> groups = new Dictionary<string, float>();
groups.Add("Group1", 10);
groups.Add("Group2", 15);
groups.Add("Group3", 20);
groups.Add("Group4", 30);
for (int i=0; i < groups.Count - 1; i++)
{
Iterate(groups, i, 0, "");
}
Console.Read();
}
private static void Iterate(Dictionary<string, float> groups, int k, float sum, string s)
{
KeyValuePair<string, float> g = groups.ElementAt(k);
if (string.IsNullOrEmpty(s))
{
s = g.Key;
}
else
{
s += " + " + g.Key;
Console.WriteLine(s + " = " + (sum + g.Value));
}
for (int i = k + 1; i < groups.Count; i++)
{
Iterate(groups, i, sum + g.Value, s);
}
}
I've asked a question about converting an integer to byte representation to solve a problem similar to this.
Converting integer to a bit representation
Here's my 10 cents. It uses the notion that I think #DK was hinting at. You take an integer and convert it to a binary number that represents a bitmask of groups to add. 1 means add it, 0 means skip it. Its in VB but should be convertible to C# pretty easily.
'//Create the group of numbers
Dim Groups As New List(Of Integer)({10, 15, 20, 30})
'//Find the total number groups (Same as 2^Groups.Count() - 1 but reads better for me)
Dim MaxCount = Convert.ToInt32(New String("1"c, Groups.Count), 2)
'//Will hold our string representation of the current bitmask (0011, 1010, etc)
Dim Bits As String
'//Will hold our current total
Dim Total As Integer
'//Will hold the names of the groups added
Dim TextPart As List(Of String)
'//Loop through all possible combination
For I = 0 To MaxCount
'//Create our bitmask
Bits = Convert.ToString(I, 2).PadLeft(Groups.Count, "0")
'//Make sure we have got at least 2 groups
If Bits.Count(Function(ch) ch = "1"c) <= 1 Then Continue For
'//Re-initialize our group array
TextPart = New List(Of String)
'//Reset our total
Total = 0
'//Loop through each bit
For C = 0 To Bits.Count - 1
'//If its a 1, add it
If Bits(C) = "1"c Then
Total += Groups(C)
TextPart.Add("Group" & (C + 1))
End If
Next
'/Output
Trace.WriteLine(Join(TextPart.ToArray(), " + ") & " = " & Total)
Next
Outputs:
Group3 + Group4 = 50
Group2 + Group4 = 45
Group2 + Group3 = 35
Group2 + Group3 + Group4 = 65
Group1 + Group4 = 40
Group1 + Group3 = 30
Group1 + Group3 + Group4 = 60
Group1 + Group2 = 25
Group1 + Group2 + Group4 = 55
Group1 + Group2 + Group3 = 45
Group1 + Group2 + Group3 + Group4 = 75
This is a fairly classic combination problem. See this post for more details:
Algorithm to return all combinations of k elements from n
Effectively what you want to do is iterate from N-choose-1 through N-choose-N and calculate the sums of each subset.
Well as already said the key to your solution lies in getting all the possible combinations! You could put something like this in a static class to register it as an extension method:
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int length = -1)
{
switch (length)
{
case -1:
foreach (var combination in Enumerable.Range(1, elements.Count()).Select(count => elements.Combinations(count)).SelectMany(c => c))
yield return combination;
break;
case 0:
yield return new T[0];
break;
default:
if (length < -1) throw new ArgumentOutOfRangeException("length");
foreach (var combination in
elements
.SelectMany((element, index) =>
elements
.Skip(index + 1)
.Combinations(length - 1)
.Select(previous => (new[] { element }).Concat(previous))))
yield return combination;
break;
}
}
... and use it like this:
static void Main(string[] args)
{
var groups = new[]
{
new Tuple<string, int>("Group1", 15),
new Tuple<string, int>("Group2", 5),
new Tuple<string, int>("Group3", 17),
};
foreach (var sum in groups
.Combinations()
.Select(x =>
string.Join(" + ", x.Select(tuple => tuple.Item1)) +
" = " +
x.Sum(tuple => tuple.Item2)))
{
Console.WriteLine(sum);
}
Console.ReadLine();
}
Output:
Group1 = 15
Group2 = 5
Group3 = 17
Group1 + Group2 = 20
Group1 + Group3 = 32
Group2 + Group3 = 22
Group1 + Group2 + Group3 = 37
Okay, the last one wasn't as straightforward as I thought. I actually tested it this time, and it gives the correct results.
void PrintInner( string output, float total, List<KeyValuePair<string, float>> children )
{
var parent = children[0];
var innerChildren = new List<KeyValuePair<string, float>>();
innerChildren.AddRange( children );
innerChildren.Remove( parent );
output += parent.Key + ":" + parent.Value.ToString();
total += parent.Value;
if( output != "" ) // Will prevent outputting "Group1:10 = 10", comment out if desired.
Console.WriteLine( output + " = " + total.ToString() );
output += " + ";
while( innerChildren.Count > 0 )
{
PrintInner( output, total, innerChildren );
innerChildren.RemoveAt( 0 );
}
}
void PrintAll()
{
var items = new List<KeyValuePair<string,float>>()
{
new KeyValuePair<string,float>>( "Group1", 10 ),
new KeyValuePair<string,float>>( "Group2", 15 ),
new KeyValuePair<string,float>>( "Group3", 20 ),
new KeyValuePair<string,float>>( "Group4", 30 )
}
while( items.Count > 0 )
{
PrintInner( "", 0, items );
items.RemoveAt( 0 );
}
}
If Group is a custom data type you can overload the +, -, *, /, =, ==, != and subsequently +=, -=, *=, and /= operators as shown here: MSDN: Operator Overloading Tutorial
If your data type is a native data type: int (Int32), long, decimal, double, or float you can do the operations you have.
To output the summation of your numbers you can use:
String.Format("{0} + {1} = {2}", Group1, Group2, (Group1 + Group2));
or
String.Format("{0} + {1} + {2} = {3}", Group1, Group2, Group3, (Group1 + Group2 + Group3));
Finally if in those examples Group is a custom data type, you would also have to overload the ToString() method so that it can display properly.
<bleepzter/>
OK, Part 2 - OO Algorithm Design?
So lets say you have the following:
public class Set: List<float>
{
public Set():base(){}
public static Set operator+(Set set1, Set set2)
{
Set result = new Set();
result.AddRange(set1.ToArray());
result.AddRange(set2.ToArray());
return result;
}
public float Sum
{
get
{
if( this.Count == 0 )
return 0F;
return this.Sum();
}
}
public override string ToString()
{
string formatString = string.Empty;
string result = string.Empty;
for(int i=0; i<this.Count; i++)
{
formatString += "{" + i.ToString() + "} + ";
}
formatString = result.TrimEnd((" +").ToCharArray()); // remove the last "+ ";
float[] values = this.ToArray();
result = String.Format(formatString, values);
return String.Format("{0} = {1}", result, this.Sum);
}
}
The object Set will have a Sum property, as well as a ToString() method that will display the sum and all of its content.
I want to display 2 sets of data on the one list box, for example, I would wont to display the 7 times table and the 8 times table on the same listbox. Here is how I get the first set of data displaying:
int awnser = 0;
int z;
z = int.Parse(textBox1.Text);
for (int i = 0; i < 11; i++)
{
awnser = z * i;
listBox6.Items.Add(z + " * " + i + " = " + awnser.ToString());
}
But how do I get a line break or separation so I can put the 8 times table just underneath?
How about this?
EDIT Insert it AFTER your loop
listBox6.Items.Add(z + " * " + i + " = " + awnser.ToString());
}
listBox6.Items.Add("--------------------");
In WPF this is easy to do using a custom template, but in WinForms I think you must do it by rendering the list items yourself.
Look at this example where they override the OnDrawItem method: http://www.syncfusion.com/FAQ/windowsforms/faq_c87c.aspx#q627q