How to optimize code that changes a value deeply nested in an object graph - c#

Below is a crude for-loop to illustrate what I need to do.
Basically, if there are any 'Variable' objects with property 'Name' containing the text "TCC#", then I want to change the 'Type' property (not the .Net type) to 'VariableType.Text'.
The code is going to run over 4800 ParsedCard variables and currently takes a stupid amount of time (about 10 minutes) to simply iterate through the list and write a line to the Debug console.
ParsedCard has
IEnumerable functions which have
IEnumerable groups which have
ParseResults which have
IEnumerable variables
This is such a simple problem but I've tried all sorts of variations using LINQ but can't find anything that performs well (less than 10 seconds).
private void AdjustTCCVariables(IList<ParsedCard> parsedCards)
{
for (var i = 0; i < parsedCards.Count; i++)
{
var parsedCard = parsedCards[i];
for (var j = 0; j < parsedCard.Functions.Count(); j++)
{
var function = parsedCard.Functions.ToList()[j];
for (var k = 0; k < function.Groups.Count(); k++)
{
var group = function.Groups.ToList()[k];
for (var l = 0; l < group.ParseResult.Variables.Count(); l++)
{
var variable = group.ParseResult.Variables.ToList()[l];
if (variable.Name.Contains("TCC#"))
{
//variable.Type = VariableType.Text;
Debug.WriteLine($"Need to change variable at [{i}][{j}][{k}][{l}]");
}
}
}
}
}
}
I've tried with this LINQ but it doesn't actually change the 'variable.Type' of the input list (I suspect because it creates a new copy of the objects in memory and the assignment isn't actually affected the 'parsedCards' IEnumerable at all:
private void AdjustTCCVariables(IEnumerable<ParsedCard> parsedCards)
{
var targetVariables =
parsedCards.SelectMany(x => x.Functions.SelectMany(z => z.Groups))
.SelectMany(x => x.ParseResult.Variables.Where(v => v.Name.Contains("TCC#")));
;
foreach (var variable in targetVariables)
{
variable.Type = VariableType.Text;
}
}

As mentioned, the bottleneck in your iterations is the .ToList() calls.
Since you mention that you only want to edit the variable.Type property, I would solve this like this.
var variables = from parsedCard in parsedCards
from function in parsedCard.Functions
from group in function.Groups
from variable in group.ParseResult.Variables
where variable.Name.Contains("TCC#")
select variable;
foreach (var variable in variables) {
variable.Type = VariableType.Text;
}
You don't need to know anything other than the variable objects that need changing, you don't need all the indexes and all the other variables. Just select what you need to know, and change it.
This way you will not know the indexes, so your Debug.WriteLine(...); line won't work.

Without knowing what the defintion of the classes , here is some tips.
Remove toList, dont count on the iteration (for statement)
int numberOf = parsedCards.Count
for (var i = 0; i < numberOf; i++)
{
//var parsedCard = parsedCards[i];
int noOf2 = parsedCard[i].Functions.Count()
for (var j = 0; j < noOf2; j++)
{
var function = parsedCard[i].Functions[j];
int = function.Groups.Count();
for (var k = 0; k < noOfGroups; k++)
{
var group = function.Groups[k];
int noOfVars = group.ParseResult.Variables.Count();
for (var l = 0; l < noOfVars; l++)
{
var variable = group.ParseResult.Variables[l];
if (variable.Name.Contains("TCC#"))
{
//variable.Type = VariableType.Text;
Debug.WriteLine($"Need to change variable at [{i}][{j}][{k}][{l}]");
}
}
}
}
}

Related

Rearranging a datatable

I'm importing an excel file into a datatable (dtImport) and rearranging that data into another datatable (dtImportParsed).
Here's what that datatable (dtImport) looks like when I first import it.
And this is how I'm trying to rearrange that datatable (dtImportParsed):
I'm currently accomplishing this by using some nested for loops, but this takes a very long time. For example, a sheet with 36 columns and 4,000 rows takes about 30-40 minutes to complete. Is there an alternative method of accomplishing this that would speed things up?
Here's my code:
for (int c = 2; c < dtImport.Columns.Count; c++) //for each date column
{
for (int r = 1; r < dtImport.Rows.Count; r++)
{
if (dtImportParsed.Rows.Count == 0)
{
DataRow dataRowImport = dtImportParsed.NewRow();
dataRowImport["Date"] = dtImport.Columns[c].ColumnName.ToString().Trim();
dataRowImport["account_id"] = dtImport.Rows[r]["account_id"].ToString().Trim();
dataRowImport[dtImport.Rows[r]["Event Name"].ToString().Trim()] = dtImport.Rows[r][c].ToString().Trim();
dtImportParsed.Rows.Add(dataRowImport);
}
else
{
for (int i = 0; i < dtImportParsed.Rows.Count; i++)
{
if (dtImportParsed.Rows[i]["account_id"].ToString() == dtImport.Rows[r]["account_id"].ToString())
{
if (dtImportParsed.Rows[i]["Date"].ToString() == dtImport.Columns[c].ColumnName.ToString())
{
dtImportParsed.Rows[i][dtImport.Rows[r]["Event Name"].ToString().Trim()] = dtImport.Rows[r][c].ToString().Trim();
break;
}
}
else if (i == dtImportParsed.Rows.Count - 1)
{
DataRow dataRowImport = dtImportParsed.NewRow();
dataRowImport["Date"] = dtImport.Columns[c].ColumnName.ToString().Trim();
dataRowImport["account_id"] = dtImport.Rows[r]["account_id"].ToString().Trim();
dataRowImport[dtImport.Rows[r]["Event Name"].ToString().Trim()] = dtImport.Rows[r][c].ToString().Trim();
dtImportParsed.Rows.Add(dataRowImport);
}
}
}
}
}
The algorithm you use to generate your expected result is too expensive! It will execute in order (c x r x i) where i > r because empty fields are injected in the final table; Actually it is an O(n3) algorithm! Also you preform it on DataTables via iterating DataRows that probably are not efficient for your requirement.
If your source data-set is not large (as you mentioned) and you have not memory restriction, I propose you to arrange expected data-set in memory using index-based data structures. Something like this:
var arrangeDragon = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();
The dragon enters! And eats the inner for.
for (int c = 2; c < dtImport.Columns.Count; c++) //for each date column
{
for (int r = 1; r < dtImport.Rows.Count; r++)
{
// ...
// instead of: for (int i = 0; i < dtImportParsed.Rows.Count; i++) ...
string date = dtImport.Columns[c].ColumnName.ToString().Trim();
string accountId = dtImport.Rows[r]["account_id"].ToString();
string eventName = dtImport.Rows [r]["Event Name"].ToString().Trim();
if (!arrangeDragon.ContainsKey(date))
arrangeDragon.Add(date, new Dictionary<string, Dictionary<string, string>>());
if (!arrangeDragon[date].ContainsKey(accountId))
arrangeDragon[date][accountId] = new Dictionary<string, string>();
if (!arrangeDragon[date][accountId].ContainsKey(eventName))
arrangeDragon[date][accountId][eventName] = dtImport.Rows[r][c].ToString().Trim();
// ...
}
}
These checks will execute in O(1) instead of O(i), so total overhead will decrease to O(n2) that is the nature of iterating table :)
Also retrieve order is O(1):
string data_field = arrangeDragon["1/1/2022"]["account1"]["Event1"];
Assert.AreEqual(data_field, "42");
Now you can iterate nested Dictionarys once and build the dtImportParsed.
If your data-set is large or host memory is low, you need other solutions that is not your problem as mentioned ;)
Good luck

How to create a string set with multiple list?

User can pass any number of list with same number of elements in it. Example- user has passed below 3 (could be dynamic with same number of elements in it) list -
hospitalId - H11, H12, H13...n
patientId - P11, P12, P13...n
statusId - S11, S13, S11...n
What is the efficient way of creating a set out of it and storing it as a string in below format? Need a c# code for it.
expected output -
"((H11,P11, S11), (H12, P12, S13), (H13, P13, S11))"
You should iterate through your list and append them index wise to prepare the result.
StringBuilder builder = new StringBuilder();
builder.Append("(");
for(var index = 0; index < n; index++)
{
builder.AppendFormat("({0}, {1}, {2})", hospitalId[index], patientId[index], statusId[index]);
}
builder.Append(")");
var result = builder.ToString();
If you have n number of List<T> items with the same length, a basic loop will do the trick. Here's a version as an extension method that will take any number of lists as an input:
public static IEnumerable<IEnumerable<T>> ZipMultiple<T>(this List<List<T>> source)
{
var counts = source.Select(s => s.Count).Distinct();
if (counts.Count() != 1)
{
throw new ArgumentException("Lists aren't the same length");
}
for (var i = 0; i < counts.First(); i++)
{
var item = new List<T>();
for (var j = 0; j < source.Count; j++)
{
item.Add(source[j][i]);
}
yield return item;
}
}
After that, it's pretty simple to convert the result into a string in another loop, or you can do it as a single liner:
var zipped = lists.ZipMultiple();
var output = $"({string.Join(", ", zipped.Select(x => $"({string.Join(",", x)})"))})";

c# value sent to Func not peristant, c# IIFE equivalent

This loop is executed
CoordPosition = new Func<string, int>[3];
for (int i = 0; i < CoordPosition.Length; i++)
{
CoordPosition[i] = (x => CapStringNumber(x, i));
}
When the method CapStringNumber is later executed by the funcs its second parameter has the value 3 for the entire array. I want it to be 0, 1, 2.
The following works:
CoordPosition = new Func<string, int>[3];
for (int i = 0; i < CoordPosition.Length; i++)
{
var localVariable = i;
CoordPosition[i] = (x => CapStringNumber(x, localVariable));
}
What is the proper way to achieve the effect I want? Making that localVariable seems a bit un-maintainable. I don't know javaScript but i'm told that 'IIFE' is a relevant javaScript term for this.

C# Sorting, return multiple text file string entries line at a time

I have a C# console window program and I am trying to sort "File3" (contains numbers) in ascending and output lines from 3 text files.
So the outcome looks something like this:
===========================================================================
field1.....................field2.....................field3
===========================================================================
[FILE1_LINE1]..............[FILE2_LINE1]..............[FILE3_LINE1]
[FILE1_LINE2]..............[FILE2_LINE2]..............[FILE3_LINE2]
[FILE1_LINE3]..............[FILE2_LINE3]..............[FILE3_LINE3]
and so on...
At the moment, it kinda works I think but it duplicates the first two lines it seems. Could someone give an example of better coding please?
Here is the code that I have atm:
string[] File1 = System.IO.File.ReadAllLines(#"FILE1.txt");
string[] File2 = System.IO.File.ReadAllLines(#"FILE2.txt");
string[] File3 = System.IO.File.ReadAllLines(#"FILE3.txt");
decimal[] File3_1 = new decimal[File3.Length];
for(int i=0; i<File3.Length; i++)
{
File3_1[i] = decimal.Parse(File3[i]);
}
decimal[] File3_2 = new decimal[File3.Length];
for(int i=0; i<File3.Length; i++)
{
File3_2[i] = decimal.Parse(File3[i]);
}
decimal number = 0;
for (double i = 0.00; i < File3_1.Length; i++)
{
for (int sort = 0; sort < File3_1.Length - 1; sort++)
{
if (File3_1[sort] > File3_1[sort + 1])
{
number = File3_1[sort + 1];
File3_1[sort + 1] = File3_1[sort];
File3_1[sort] = number;
}
}
}
if (SortChoice2 == 1)
{
for (int y = 0; y < File3_2.Length; y++)
{
for (int s = 0; s < File3_2.Length; s++)
{
if (File3_1[y] == File3_2[s])
{
Console.WriteLine(File1[s] + File2[s] + File3_1[y]);
}
}
}
}
Just for more info, most of this code was used for another program and worked but in my new program, this doesn't as I've said above - ("it repeats a couple of lines for some reason"). I'm kinda an amateur/ rookie at C# so I only get stuff like this to work with examples.
Thanks in advance :)
Ok, if I understand correctly, what you are trying to do is read the lines from 3 different files, each of them representing a different "field" in a table. You then want to sort this table based on the value of one of the field (in you code, this seems to be the field which values are contained in File3. Well, if I got that right, here's what I suggest you do:
// Read data from files
List<string> inputFileNames = new List<string> {"File1.txt", "File2.txt", "File3.txt"};
decimal[][] fieldValues = new decimal[inputFileNames.Count][];
for (int i = 0; i < inputFileNames.Count; i++)
{
string currentInputfileName = inputFileNames[i];
string[] currentInputFileLines = File.ReadAllLines(currentInputfileName);
fieldValues[i] = new decimal[currentInputFileLines.Length];
for (int j = 0; j < currentInputFileLines.Length; j++)
{
fieldValues[i][j] = decimal.Parse(currentInputFileLines[j]);
}
}
// Create table
DataTable table = new DataTable();
DataColumn field1Column = table.Columns.Add("field1", typeof (decimal));
DataColumn field2Column = table.Columns.Add("field2", typeof (decimal));
DataColumn field3Column = table.Columns.Add("field3", typeof (decimal));
for (int i = 0; i < fieldValues[0].Length; i++)
{
var newTableRow = table.NewRow();
newTableRow[field1Column.ColumnName] = fieldValues[0][i];
newTableRow[field2Column.ColumnName] = fieldValues[1][i];
newTableRow[field3Column.ColumnName] = fieldValues[2][i];
table.Rows.Add(newTableRow);
}
// Sorting
table.DefaultView.Sort = field1Column.ColumnName;
// Output
foreach (DataRow row in table.DefaultView.ToTable().Rows)
{
foreach (var item in row.ItemArray)
{
Console.Write(item + " ");
}
Console.WriteLine();
}
Now, I tried to keep the code above as LINQ free as I could, since you do not seem to be using it in your example, and therefore might not know about it. That being said, while there is a thousand way to do I/O in C#, LINQ would help you a lot in this instance (and in pretty much any other situation really), so I suggest you look it up if you don't know about it already.
Also, the DataTable option I proposed is just to provide a way for you to visualize and organize the data in a more efficient way. That being said, you are in no way obliged to use a DataTable: you could stay with a more direct approach and use more common data structures (such as lists, arrays or even dictionaries if you know what they are) to store the data, depending on your needs. It's just that with a DataTable, you don't, for example, need to do the sorting yourself, or deal with columns indexed only by integers. With time, you'll come to learn about the myriad of useful data structure and native functionalities the C# language offers you and how they can save you doing the work yourself in a lot of cases.

Outputting a C# Object list [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 8 years ago.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Improve this question
I have
List<dynamic> events = new List<dynamic>();
for (int i = 0; i < 3000; i++)
{
if (i % 2 == 0) //is even
{
var dataStart = new
{
TimeId = startTime.AddSeconds(i),
ValueStart = i
};
}
}
how can I output what is in the List object? I am very new to C# so I have tried
for (int j = 0; j < events.Count; j++)
{
Console.WriteLine(events);
}
but this is not working. Any ideas as to what I need to do to correctly output the values of the object?
First, add created items into the list:
List<dynamic> events = new List<dynamic>();
for (int i = 0; i < 3000; i++)
if (i % 2 == 0) {//is even
events.Add( // <- Do not forget Add
new {
TimeId = startTime.AddSeconds(i),
ValueStart = i }
);
Then print the list out
foreach(var item in events)
if (!Object.ReferenceEquals(null, item))
Console.WriteLine("time: {0} value: {1}", item.TimeId, item.ValueStart);
First of all you need to understand that you're having a List of dynamic type (I don't know what that is, maybe you're local Class file). It is initialized as
List<dynamic> events = new List<dynamic>();
Now, the events would have a list of elements that would be dynamic objects.
When you need to output it, you need to reference it. You need this to do that
for (int j = 0; j < events.Count; j++)
{
Console.WriteLine(events[j].SomeProperty.ToString());
}
This code would get some property of each of the dynamic object inide the events list and then it would write it in the Console.
Two things:
You forgot to add your elements to the list:
DateTime startTime = DateTime.Now;
List<dynamic> events = new List<dynamic>();
for (int i = 0; i < 3000; i++)
{
if (i % 2 == 0) //is even
{
var dataStart = new
{
TimeId = startTime.AddSeconds(i),
ValueStart = i
};
events.Add(dataStart); // <--
}
}
Just use a foreach for writing to the console like this:
foreach (var dataPoint in events)
{
Console.WriteLine(dataPoint);
}
EDIT: Since I lost the race, I may as well present a more interesting way to do this:
(This also has the bonus of being backwards compatible to C# 2, whereas dynamic only exists from C# 4 on.)
public static IEnumerable<object> GetEvents(DateTime startTime)
{
for (int i = 0; i < 3000; i++)
{
if (i % 2 == 0) //is even
{
yield return new
{
TimeId = startTime.AddSeconds(i),
ValueStart = i
};
}
}
}
You would call this like so:
DateTime startTime = DateTime.Now;
foreach (var dataPoint in GetEvents(startTime))
{
Console.WriteLine(dataPoint);
}
More on yield return in this answer by Jon Skeet.
You're nearly there, just need to access the element via index (assuming you have anything in the collection):
for (int j = 0; j < events.Count; j++)
{
Console.WriteLine(events[j].FooProperty);
}

Categories