Extracting repeated for loop functionality into own method - c#

Within one of my classes I have a number of methods which all have similar purposes; these methods convert different objects into valid JSON representation. Each method does slightly different things as the objects being fed into the method are different and therefore their JSON output will also be subtly different.
Within these methods a for loop exists, the purpose of this loop is to check whether or not the field being converted into JSON is the last one in the object, if it is not then a , will be placed after the converted JSON string, as is normal within JSON.
Below is an example of one of these for loops:
for (int i = 0; i < numberOfSections; i++)
{
if (i == numberOfSections - 1)
{
output += SectionToJson(root.Sections[i]);
}
else
{
output += SectionToJson(root.Sections[i]);
output += ",";
}
}
One thing to note here is that the call to the method (SectionToJson here) is different within each method. Therefore I have three different for loops doing almost the same thing but with different method calls inside their clauses.
I want to know whether or not there is a way that I can remove these ugly for loops from my three different methods and instead place their functionality inside a single method which can then be called from the three methods instead. However since the internal method call is different within each method, it makes it more difficult to place inside a single method.
I considered using the Func delegate to pass the required method through as a parameter to the new method, but this would not work as the parameters for the three internal methods are all different, and therefore I would need three different overrides of a single method. Which kinda defeats the point in removing the for loops in the first place.
Is there any other approach that I haven't considered that would help me achieve my goal here? I'm also trying to keep my list of parameters down, and would rather not go over three parameters in the new method. Preferably two.
The other two for loops in question are below.
for (int i = 0; i < numberOfQuestionsInBank; i++)
{
if (i == numberOfQuestionsInBank - 1)
{
output += QuestionPropertyToJson(questionBank.Properties[i]);
}
else
{
output += QuestionPropertyToJson(questionBank.Properties[i]);
output += ",";
}
}
for (int i = 0; i < numberOfSections; i++)
{
if (i == numberOfSections - 1)
{
requiredSections += "\"" + (i+1) + "\"";
}
else
{
requiredSections += "\"" + (i+1) + "\"";
requiredSections += ",";
}
}

Well, there is another way - use string.Join this is exactly what it does.
And of course with linq you can make it look quite nice:
string.Join(",", root.Sections.Select(SectionToJson))
string.Join accepts collections of strings, so you keep concentrate on your conversion to strings (for each element) and let it do the concatenation for you.

The real-world solution to your problem is to use a JSON serializer, but of course this is an exercise so we can treat it a little differently.
What we need to do is look at the code an find the parts that are most alike. Once we locate that part of the code, we need to make it even more alike. Once they are identical, we can remove the duplication. They both remain 'til they're both the same.
So first of all, let's change both functions slightly to move the part that is different from the part that is almost the same. You can see that the if-statement in both is starting to look very similar.
for (int i = 0; i < numberOfQuestionsInBank; i++)
{
output += QuestionPropertyToJson(questionBank.Properties[i]);
if (i != numberOfQuestionsInBank - 1)
{
output += ",";
}
}
And
for (int i = 0; i < numberOfSections; i++)
{
requiredSections += "\"" + (i + 1) + "\"";
if (i != numberOfSections - 1)
{
requiredSections += ",";
}
}
Now let's consider what would happen if the naming was the same. Note: I'm not saying that you should make your naming the same everywhere as this would make your code less expressive - but we can do this exercise in our heads...
for (int i = 0; i < recordCount; i++)
{
output += QuestionPropertyToJson(questionBank.Properties[i]);
if (i != recordCount - 1)
{
output += ",";
}
}
And
for (int i = 0; i < recordCount; i++)
{
output += "\"" + (i + 1) + "\"";
if (i != recordCount - 1)
{
output += ",";
}
}
The section:
if (i != recordCount - 1)
{
output += ",";
}
Is now identical in both... we could create a function for that. There are a few ways to do that, this is just one of them:
public string ConditionalComma(int recordCount, int i)
{
if (i != recordCount - 1)
{
return ",";
}
return string.Empty;
}
That means our methods now look like this (I'll keep the in-head naming):
for (int i = 0; i < recordCount; i++)
{
output += QuestionPropertyToJson(questionBank.Properties[i]) + ConditionalComma(recordCount, i);
}
And
for (int i = 0; i < recordCount; i++)
{
output += "\"" + (i + 1) + "\"" + ConditionalComma(recordCount, i);
}
So we have managed to extract the differences and remove the duplication in a sensible way.
That's probably far enough for this exercise, but feel free to ask questions.

Related

Accumulating values from a listbox with a for loop instead of if statements

I have learned how to accumulate totals with if statements and now I am trying to shorten up my code.
A friend of mine told me that you can use a FOR loop to do this so I tried it out but I am having trouble understanding FOR loops completely.
My IF Code is this...
if (lbSnacks.Items[0].Selected)
{
decSnackSaleTotal = decSnackSaleTotal + Convert.ToDecimal(lbSnacks.Items[0].Value);
}
if (lbSnacks.Items[1].Selected)
{
decSnackSaleTotal = decSnackSaleTotal + Convert.ToDecimal(lbSnacks.Items[1].Value);
}
if (lbSnacks.Items[2].Selected)
{
decSnackSaleTotal = decSnackSaleTotal + Convert.ToDecimal(lbSnacks.Items[2].Value);
}
if (lbSnacks.Items[3].Selected)
{
decSnackSaleTotal = decSnackSaleTotal + Convert.ToDecimal(lbSnacks.Items[3].Value);
}
if (lbSnacks.Items[4].Selected)
{
decSnackSaleTotal = decSnackSaleTotal + Convert.ToDecimal(lbSnacks.Items[4].Value);
}
if (lbSnacks.Items[5].Selected)
{
decSnackSaleTotal = decSnackSaleTotal + Convert.ToDecimal(lbSnacks.Items[5].Value);
}
Of course, I commented it out to try to use the for loop.
The loop I used is this:
for (int i = 0; i < lbSelected.Items.Count; i++)
{
decSnackSaleTotal += Convert.ToDecimal(lbSelected.Items[i]);
}
lbSelected is my second listbox where the selected items go from the customer.
lbSnacks is my first listbox where the snack selection is.
Thanks!
Probably you cannot convert the item to decimal, it seems you have to read the Value property as with the if approach.
for (int i = 0; i < lbSelected.Items.Count; i++){
decSnackSaleTotal += Convert.ToDecimal(lbSelected.Items[i].Value);
}
That approach should do what you want, although you need to access the Value property in your loop too, similar to your if statements:
decSnackSaleTotal += Convert.ToDecimal(lbSelected.Items[i].Value);
This is functionally different than what your if statements are doing though.
In the former case, you're only adding a value to the total if it was selected.
In the latter case, you're adding all values regardless of whether or not the are selected.
You may want to check for that inside your loop.
There are other ways to approach the problem too, but this is at least better than checking every element one at a time.

How to separate objects in a hidden input?

I have a hidden input that contains some objects in it. I put string "#" between any two objects, but I want to put a string that isn't on keyboard. How can I do it?
for (int i = 0; i < MyTable.Rows.Count; i++)
{
txtRows.Value += MyTable.Rows[i]["Row"].ToString();
if (i < MyTable.Rows.Count - 1)
{
txtRows.Value += "#";
}
}
You can put any character you like in between two strings. For example to use the ASCII record separator character, use this:
if (i < MyTable.Rows.Count - 1)
{
txtRows.Value += '\x1e';
}
And then split the value back into multiple strings using the Split method:
string[] values = txtRows.Split('\x1e');
However, I'd recommend using an array or list of inputs instead:
for (int i = 0; i < MyTable.Rows.Count; i++)
{
txtRows[i].Value = MyTable.Rows[i]["Row"].ToString();
}
Of course, you'll probably have to modify how you add these hidden elements to your form, but it's a much nicer way of handling these sorts of problems.

How can i optimize this c# code?

I have converted my Datatable to json string use the following method...
public string GetJSONString(DataTable Dt)
{
string[] StrDc = new string[Dt.Columns.Count];
string HeadStr = string.Empty;
for (int i = 0; i < Dt.Columns.Count; i++)
{
StrDc[i] = Dt.Columns[i].Caption;
HeadStr += "\"" + StrDc[i] + "\" : \"" + StrDc[i] + i.ToString() + "¾" + "\",";
}
HeadStr = HeadStr.Substring(0, HeadStr.Length - 1);
StringBuilder Sb = new StringBuilder();
Sb.Append("{\"" + Dt.TableName + "\" : [");
for (int i = 0; i < Dt.Rows.Count; i++)
{
string TempStr = HeadStr;
Sb.Append("{");
for (int j = 0; j < Dt.Columns.Count; j++)
{
if (Dt.Rows[i][j].ToString().Contains("'") == true)
{
Dt.Rows[i][j] = Dt.Rows[i][j].ToString().Replace("'", "");
}
TempStr = TempStr.Replace(Dt.Columns[j] + j.ToString() + "¾", Dt.Rows[i][j].ToString());
}
Sb.Append(TempStr + "},");
}
Sb = new StringBuilder(Sb.ToString().Substring(0, Sb.ToString().Length - 1));
Sb.Append("]}");
return Sb.ToString();
}
Is this fair enough or still there is margin for optimization to make it execute faster.... Any suggestion...
Before asking if you can optimise it to make it execute faster, the first question you need to ask yourself is, does it run fast enough for me? Premature optimisation is the curse of all of us (I know I've done it!). You could spend hours trying to micro-optimise this code, which might take it from taking, for example, 20ms to execute down to 15ms. Yes that'd be a reduction of 25%, but would 5ms really be worth 2 hours of your time? More importantly, would it provide enough of a benefit to your end users to warrant it?
Have you considered using the JsonSerializer from "Newtonsoft"? This may well be "quick enough", is fairly widely used and is thus more likely to be correct overall than anything I, or you, can write first time round.
Purely from a readability perspective (that may also allow the C# compiler / CLR to improve thing for you) you could consider changing long bits of string concatenation such as:
HeadStr += "\"" + StrDc[i] + "\" : \"" + StrDc[i] + i.ToString() + "¾" + "\",";
To:
HeadStr += string.Format("\"{0}\" : \"{0}{1}¾\",", strDc[i], i);
But for any changes you do make. Measure, Rinse, Repeat =)
There may well be ways of getting it to execute faster - but do you have any indication that you need it to execute faster? Do you have a good reason to believe this is a significant bottleneck in your code? If so, benchmark the code with some real data and profile the routine to work out where the time is going.
You could tidy up some bits:
Use string.Format() to avoid long x + y + z sequences. This may or may not make things faster (it would be marginal either way).
You usually don't need .toString() when concatenating.
You could also pass in the StringBuffer to be populated, so that the caller might have the opportunity to bundle up several such operations into a single StringBuffer.
These suggestions are focused more on tidiness than performance, which I think should be the real focus unless this code is presenting as a bottleneck in your profiling.
Why do you think it needs optimization? Is it really slow on some DataTables?
I'd just serialize DataTable with something like newton JSON serializer, if it's serializable at all.
Refactor your code, use a tool like ReSharper, JustCode etc to tidy it up a bit. Extract methods and use individual tests ( Test Driven Development-ish ) to find bottlenecks in your code and then tweak those.
But your first step should be: Refactor!
The problem with the code isn't speed, but that it's not cleaned up. I've done some clean-up, but you could probably do even more:
public string GetJSONString2(DataTable table)
{
StringBuilder headStrBuilder = new StringBuilder(table.Columns.Count * 5); //pre-allocate some space, default is 16 bytes
for (int i = 0; i < table.Columns.Count; i++)
{
headStrBuilder.AppendFormat("\"{0}\" : \"{0}{1}¾\",", table.Columns[i].Caption, i);
}
headStrBuilder.Remove(headStrBuilder.Length - 1, 1); // trim away last ,
StringBuilder sb = new StringBuilder(table.Rows.Count * 5); //pre-allocate some space
sb.Append("{\"");
sb.Append(table.TableName);
sb.Append("\" : [");
for (int i = 0; i < table.Rows.Count; i++)
{
string tempStr = headStrBuilder.ToString();
sb.Append("{");
for (int j = 0; j < table.Columns.Count; j++)
{
table.Rows[i][j] = table.Rows[i][j].ToString().Replace("'", "");
tempStr = tempStr.Replace(table.Columns[j] + j.ToString() + "¾", table.Rows[i][j].ToString());
}
sb.Append(tempStr + "},");
}
sb.Remove(sb.Length - 1, 1); // trim last ,
sb.Append("]}");
return sb.ToString();
}
I would suggest a different solution,if you are using .net 3.0 or 3.5
instead of doing this
Convert datatable into xml
use xmlserializer to convert the xml to your domain object
Using JavaScriptSerializer(System.Web.Extensions.dll) to serialize the domain object to json string.

Returning arrays

main()
{
....
i = index;
while (i < j)
{
if (ip[i] == "/")
{
ip[i - 1] = (double.Parse(ip[i - 1]) / double.Parse(ip[i + 1])).ToString();
for (int k = i; k < (ip.Length - 2); k++)
{
ip[k] = ip[k + 2];
}
Array.Resize(ref ip, ip.Length - 2);
j = j - 2;
i--;
}
i++;
}
}
For the above code I wanted to apply Oop's concepts.
This pattern repeats almost 5 times (for div,mul,add,sub,pow) in main program, with four identical lines .
To decrease the no of lines and there by to increase efficiency of code, I wrote the same like this.
i = index;
while (i < j)
{
if (ip[i] == "/")
{
ip[i - 1] = (double.Parse(ip[i - 1]) / double.Parse(ip[i + 1])).ToString();
ext.Resize(ip, i, j);
}
i++;
}
class ext
{
public static void Resize(string [] ip, int i, int j)
{
for (int k = i; k < (ip.Length - 2); k++) { ip[k] = ip[k + 2]; }
Array.Resize(ref ip, ip.Length - 2);
j=j-2; i--;
return ;
}
}
Code got compiled successfully. But the problem is the changes in array and variables that took place in called function are not reflecting in main program. The array and variables are remaining unchanged in main program.
I am unable to understand where I went wrong.
Plz guide me.
Thank You.
I don't think you understand what ref parameters are for - once you understand those (and the fact that arrays themselves can't change in size), you'll see why Array.Resize takes a ref parameter. Have a look at my parameter passing article for details.
You can fix your code by changing it like this:
public static void Resize(ref string [] ip, ref int i)
{
for (int k = i; k < (ip.Length - 2); k++)
{
ip[k] = ip[k + 2];
}
Array.Resize(ref ip, ip.Length - 2);
j = j - 2;
i--;
}
and calling it like this:
ext.Resize(ref ip, ref i);
However, I suspect that using a more appropriate data structure would make your code clearer. Is there any reason you can't use a List<string> instead?
You're removing items from the middle of a sequence, so shrinking its length. So using arrays is just making it difficult.
If ip was a List<string> instead of string[]:
i = index;
while (i < j)
{
if (ip[i] == "/")
{
ip[i - 1] = (double.Parse(ip[i - 1]) / double.Parse(ip[i + 1])).ToString();
ip.RemoveAt(i);
ip.RemoveAt(i);
j = j - 2;
i--;
}
i++;
}
It looks like you're parsing an arithmetic expression. However, you might want to allow for parentheses to control the order of evaluation, and that's going to be tricky with this structure.
Update: What your code says is: You are going to scan through a sequence of strings. Anywhere in that sequence you may find a division operator symbol: /. If so, you assume that the things on either side of it can be parsed with double.Parse. But:
( 5 + 4 ) / ( 6 - 2 )
The tokens on either side of the / in this example are ) and ( so double.Parse isn't going to work.
So I'm really just checking that you have another layer of logic outside this that deals with parentheses. For example, perhaps you are using recursive descent first, and then only running the piece of code you posted on sequences that contain no parentheses.
If you want the whole thing to be more "objecty", you could treat the problem as one of turning a sequence of tokens into a tree. Each node in the tree can be evaluated. The root node's value is the value of the whole expression. A number is a really simple node that evaluates to the number value itself. An operator has two child nodes. Parenthesis groups would just disappear from the tree - they are used to guide how you build it. If I have some time later I could develop this into a short example.
And another question: how are you splitting the whole string into tokens?

klocwork reports issues with concatenation in a loop

String concatenation in a loop. I get this bug when i tested my project in klocwork. i am concatenating many strings in loop is it a big mistake .
for(int j=0;j<nAttr;j++)
{
builder = new StringBuilder();
size=rnd.Next(1,10);
for(int k=0; k<size; k++)
{
ch = Convert.ToChar(Convert.ToInt32(26 * rnd.NextDouble() + 65)) ;
if(ch=='[' || ch==']')
j--;
else
builder.Append(ch);
}
strXml+=" "+builder.ToString();//here the bug arises
strXml+="="+"\"";
i also found this bug when i tested
Class/struct data member is hidden by a local variable what does it mean
private void TraverseValues(XmlNode n,ArrayList arr)
{
if(n.HasChildNodes )
{
for(int i=0;i<n.ChildNodes.Count;i++)
{
if(n.ChildNodes[i].Name=="#text")
arr.Add(n.ChildNodes[i].InnerText );
else
TraverseValues(n.ChildNodes[i],arr)//here the code arises
i completed my project .it works fine then i tested my product with klocwork it suggested these bugs .but i am not able to understand y it says it has critical error in code
Klickwork creates a static analysis application.
I think Arunachalam refers to some static analysis assertion as "bug".
When strXml is of string type then it would be possible that static analysis will show strXml+=" "+builder.ToString(); as bad code (a "bug").
From the code posted it is definitely not bug in the core .NET Library.
It sounds like you might be defining a variable in your loop that has the same name as one in the class?
What type of Bug do you expect there to be? Could you post more of your code, so that we can se, what should come out of this operation?
I just scrapped your code into a new project, cleaned the code a little bit up, but it seems to work like expected.
StringBuilder builder;
int nAttr = 5;
int size;
char ch;
Random rnd = new Random();
for(int j=0;j<nAttr;j++)
{
builder = new StringBuilder();
builder.Append(" ");
size=rnd.Next(1,10);
for(int k=0; k<size; k++)
{
ch = Convert.ToChar(Convert.ToInt32(26 * rnd.NextDouble() + 65)) ;
if(ch=='[' || ch==']')
j--;
else
builder.Append(ch);
}
builder.Append("=\"");
Console.WriteLine(builder.ToString());
}
One thing I do not understand is why do you use a StringBuilder, then concatenate the result of the StringBuilder with a literal to a new string and concatenate that again. You should use your StringBuilder to build the whole string, the export the result.
You might want to have
if (ch=='[' || ch==']')
k--;
But even this doesn't make sense, since you will only get characters from 'A' to 'Z'.
If I understand it correctly, it sounds like your tool is trying to tell you to only update strXml once:
builder = new StringBuilder();
for(int j=0;j<nAttr;j++)
{
builder.Append(" ");
size=rnd.Next(1,10);
for(int k=0; k<size; k++)
{
ch = Convert.ToChar(Convert.ToInt32(26 * rnd.NextDouble() + 65)) ;
if(ch=='[' || ch==']')
j--;
else
builder.Append(ch);
}
builder.Append("\"");
//...
}
strXml += builder.ToString();

Categories