Related
I'm starting with programming and C# and I have two tuples. One tuple is representing a list of points:
static List<(string, string, string)> PR { get; set; } = new List<(string, string, string)>()
{
("P1", "0", "0"),
("P2", "P1", "P1+Height"),
("P3", "P1+Width", "P2"),
("P4", "P3", "P3+Height")
};
where Item1 in the list of tuples stands for a Point name (P1, P2, P3, P4) and Item2 and Item3 represent a parametric formula for respectively the x- and y-value of a point.
"P1" in the second item in the above list should look for the tuple starting with "P1", and then for the second item in that tuple, in this case, 0.
I have a second list of tuples that represent the parameters that I need to calculate the above point values.
static List<(string, double)> PAR { get; set; } = new List<(string, double)>()
{
("Height", 500),
("Width", 1000)
};
Say I want to calculate the value of the parametric formula "P3+Height" as follows:
P3+Height --> P2 (+Height) --> P1+Height (+Height) --> 0 (+Height (+Height) --> 0 + Height + Height;
In the end I want to replace the parameter strings with the actual values (0 + 500 + 500 -> P3+Height = 1000) but thats of later concern.
Question: I'm trying to make a function that recursively evaluates the list of tuples and keeps the parameter names, but also looks for the corresponding tuple until we reach an end or exit situation. This is where I'm at now but I have a hard time getting my thought process in actual working code:
static void Main(string[] args)
{
//inputString = "P3+Height"
string inputString = PR[3].Item3;
string[] returnedString = GetParameterString(inputString);
#region READLINE
Console.ReadLine();
#endregion
}
private static string[] GetParameterString(string inputString)
{
string[] stringToEvaluate = SplitInputString(inputString);
for (int i = 0; i < stringToEvaluate.Length; i++)
{
//--EXIT CONDITION
if (stringToEvaluate[0] == "P1")
{
stringToEvaluate[i] = "0";
}
else
{
if (i % 2 == 0)
{
//Check if parameters[i] is point string
var value = PAR.Find(p => p.Item1.Equals(stringToEvaluate[i]));
//Check if parameters[i] is double string
if (double.TryParse(stringToEvaluate[i], out double result))
{
stringToEvaluate[i] = result.ToString();
}
else if (value == default)
{
//We have a point identifier
var relatingPR = PR.Find(val => val.Item1.Equals(stringToEvaluate[i])).Item2;
//stringToEvaluate[i] = PR.Find(val => val.Item1.Equals(pointId)).Item2;
stringToEvaluate = SearchParameterString(relatingPR);
}
else
{
//We have a parameter identifier
stringToEvaluate[i] = value.Item2.ToString();
}
}
}
}
return stringToEvaluate;
}
private static string[] SplitInputString(string inputString)
{
string[] splittedString;
splittedString = Regex.Split(inputString, Delimiters);
return splittedString;
}
Can anyone point me in the right direction of how this could be done with either recursion or some other, better, easier way?
In the end, I need to get a list of tuples like this:
("P1", "0", "0"),
("P2", "0", "500"),
("P3", "1000", "500"),
("P4", "1000", "1000")
Thanks in advance!
I wrote something that does this - I changed a bit of the structure to simplify the code and runtime, but it still returns the tuple you expect:
// first I used dictionaries so we can search for the corresponding value efftiantly:
static Dictionary<string, (string width, string height)> PR { get; set; } =
new Dictionary<string, (string width, string height)>()
{
{ "P1", ("0", "0") },
{ "P2", ("P1", "P1+Height")},
{ "P3", ("P1+Width", "P2") },
{ "P4", ("P3", "P3+Height") }
};
static Dictionary<string, double> PAR { get; set; } = new Dictionary<string, double>()
{
{ "Height", 500 },
{ "Width", 1000 }
};
static void Main(string[] args)
{
// we want to "translate" each of the values height and width values
List<(string, string, string)> res = new List<(string, string, string)>();
foreach (var curr in PR)
{
// To keep the code generic we want the same code to run for height and width-
// but for functionality reasons we need to know which it is - so sent it as a parameter.
res.Add((curr.Key,
GetParameterVal(curr.Value.width, false).ToString(),
GetParameterVal(curr.Value.height, true).ToString()));
}
#region READLINE
Console.ReadLine();
#endregion
}
private static double GetParameterVal(string inputString, bool isHeight)
{
// for now the only delimiter is + - adapt this and the code when \ as needed
// this will split string with the delimiter ("+height", "-500", etc..)
string[] delimiters = new string[] { "\\+", "\\-" };
string[] stringToEvaluate =
Regex.Split(inputString, string.Format("(?=[{0}])", string.Join("", delimiters)));
// now we want to sum up each "part" of the string
var sum = stringToEvaluate.Select(x =>
{
double result;
int factor = 1;
// this will split from the delimiter, we will use it as a factor,
// ["+", "height"], ["-", "500"] etc..
string[] splitFromDelimiter=
Regex.Split(x, string.Format("(?<=[{0}])", string.Join("|", delimiters)));
if (splitFromDelimiter.Length > 1) {
factor = int.Parse(string.Format($"{splitFromDelimiter[0]}1"));
x = splitFromDelimiter[1];
}
if (PR.ContainsKey(x))
{
// if we need to continue recursively translate
result = GetParameterVal(isHeight ? PR[x].height : PR[x].width, isHeight);
}
else if (PAR.ContainsKey(x))
{
// exit condition
result = PAR[x];
}
else
{
// just in case we didnt find something - we should get here
result = 0;
}
return factor * result;
}).Sum();
return sum;
}
}
}
I didnt add any validity checks, and if a value wasn't found it recieves a val of 0, but go ahead and adapt it to your needs..
Here a a working example for your question... It took me a lot of time so I hope you appreciate it: The whole code is comented line by line. If you have any question do not hesitate to ask me !
First of all we create a class named myEntry that will represent an entry. The name has to be unique e.g P1, P2, P3
public class myEntry
{
public string Name { get; private set; } //this field should be unique
public object Height { get; set; } //Can contain a reference to another entry or a value also as many combinations of those as you want.
// They have to be separated with a +
public object Width { get; set; } //same as for height here
public myEntry(string name, object height, object width)
{
//Set values
this.Name = name;
this.Height = height;
this.Width = width;
}
}
Now I create a dummy Exception class for an exception in a further class (you will see the use of this further on. Just ignore it for now)
public class UnknownEntry : Exception
{
//Create a new Class that represents an exception
}
Now we create the important class that will handle the entries and do all the work for us. This might look complicated but if you don't want to spend time understanding it you can just copy paste it, its a working solution!
public class EntryHolder
{
private Dictionary<string, double> _par = new Dictionary<string, double>(); //Dictionary holding our known variables
private List<myEntry> _entries; //List holding our entries
public EntryHolder()
{
_entries = new List<myEntry>(); //Create list
//Populate dictionary
_par.Add("Height", 500);
_par.Add("Width", 1000);
}
public bool Add(myEntry entry)
{
var otherEntry = _entries.FirstOrDefault(x => x.Name.Equals(entry.Name)); //Get entry with same name
if(otherEntry != null)
{
//Entry with the same name as another entry
//throw new DuplicateNameException(); //Throw an exception if you want
return false; //or just return false
}
//Entry to add is valid
_entries.Add(entry); //Add entry
return true; //return success
}
public void Add(List<myEntry> entries)
{
foreach (var entry in entries) //Loop through entries
{
Add(entry);
}
}
public myEntry GetEntry(string uniqueName)
{
var entry = GetRawEntry(uniqueName); //Get raw entry
var heightToCalculate = entry.Height.ToString(); //Height to calculate to string
var widthToCalculate = entry.Width.ToString(); //Width to calculate to string
entry.Height = Calculate(heightToCalculate, true); //Calculate height
entry.Width = Calculate(widthToCalculate, false); //Calculate width
return entry; //return entry
}
public List<myEntry> CalculateAllEntries()
{
List<myEntry> toReturn = new List<myEntry>(); //Create list that we will return after the calculation finished
foreach (var entryToCalculate in _entries) //Loop through all entries
{
toReturn.Add(GetEntry(entryToCalculate.Name)); //calculate entry values and add them to the list we will return after
}
return toReturn; //return list after the whole calculation finished
}
private double Calculate(string toCalculate, bool isHeight)
{
if (!toCalculate.Contains("+"))
{
//String doesn't contain a + that means it has to be a number or a key in our dictionary
object toConvert = toCalculate; //Set the object we want to convert to double
var entryCorrespondingToThisValue = _entries.FirstOrDefault(x => x.Name.Equals(toCalculate)); //Check if the string is a reference to another entry
if (entryCorrespondingToThisValue != null)
{
//It is the name of another object
toConvert = isHeight ? entryCorrespondingToThisValue.Height : entryCorrespondingToThisValue.Width; //Set object to convert to the height or width of the object in entries
}
try
{
return Convert.ToDouble(toConvert); //try to convert and return if success
}
catch (Exception e)
{
//the given height object has the wrong format
//Format: (x + Y + z ...)
throw new FormatException();
}
}
//Contains some +
var spitedString = toCalculate.Split(new char[] {'+'}); //Split
double sum = 0d; //Whole sum
foreach (var splited in spitedString) //Loop through all elements
{
double toAdd = 0; //To add default = 0
if (_par.ContainsKey(splited)) //Check if 'splited' is a key in the dictionary
{
//part of object is in the par dictionary so we get the value of it
toAdd = _par[splited]; //get value corresponding to key in dictionary
}
else
{
//'splited' is not a key in the dictionary
object toConvert = splited; //set object to convert
var entryCorrespondingToThisValue = _entries.FirstOrDefault(x => x.Name.Equals(splited)); //Does entries contain a reference to this value
if (entryCorrespondingToThisValue != null)
{
//It is a reference
toConvert = isHeight ? entryCorrespondingToThisValue.Height : entryCorrespondingToThisValue.Width; //Set to convert to references height or width
}
try
{
toAdd = Convert.ToDouble(toConvert); //Try to convert object
}
catch (Exception e)
{
//A part of the given height is not a number or is known in the par dictionary
throw new FormatException();
}
}
sum += toAdd; //Add after one iteration
}
return sum; //return whole sum
}
public myEntry GetRawEntry(string uniqueName)
{
var rawEntry = _entries.FirstOrDefault(x => x.Name.Equals(uniqueName)); //Check for entry in entries by name (unique)
if (rawEntry == null)
{
//Entry is not in the list holding all entries
throw new UnknownEntry(); //throw an exception
return null; //Or just return null
}
return rawEntry; //return entry
}
}
And here the end, the test and prove that it works:
public void TestIt()
{
List<myEntry> entries = new List<myEntry>()
{
new myEntry("P1", 0, 0),
new myEntry("P2", "P1", "P1+Height"),
new myEntry("P3", "P1+Height", "P2"),
new myEntry("P4", "P3", "P3+Height"),
};
EntryHolder myEntryHolder = new EntryHolder();
myEntryHolder.Add(entries);
var calculatedEntries = myEntryHolder.CalculateAllEntries();
}
Here an image of how it looks like:
I have a method which I call CalculatePopularityScore. It exists on a Story object. The Story object has a field which is an ICollection of Comment objects.
public virtual ICollection<Comment> Comments { get; set; }
The Comment object has another collection of Reply objects.
My method looks at the story, loops through the comments associated with that story, and if the story's comments has replies, adds up that total. That, along with some other fields, gives me a very (and I stress this) very rudimentary algorithm of a story's popularity.
public double CalculateStoryPopularityScore()
{
if (Comments == null) throw new ArgumentException("Comments can't be null");
if (Comments.Count < 0) throw new ArgumentException("Comments can't be less than zero.");
int ReplyCountSum = 0;
double ReplyScore;
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double viewCount;
foreach (var comment in Comments)
{
int replyCount;
if (comment.Replies == null)
{
throw new ArgumentNullException("Replies cannot be null");
}
if (comment.Replies.Count() == 0)
{
replyCount = 0;
} else
{
replyCount = comment.Replies.Count();
}
ReplyCountSum += replyCount;
}
ReplyScore = ReplyCountSum * 4;
CommentScore = Comments.Count() * 4;
InsightfulVoteScore = InsightFulVoteCount * 3;
UsefulVoteScore = UsefulVoteCount * 2;
viewCount = ViewCount;
double PopularityScore = CommentScore + ReplyScore + InsightfulVoteScore + + UsefulVoteScore + viewCount;
return PopularityScore;
}
This seems to work fine. Now, what I'd like to do is take this method and apply it to a number of stories (i.e. a list).
I currently have this method written. It has not yet implemented another loop to look through the replies to the comments collection of a story. I know nested loops are considered bad and slow. What would be the most efficient way to look at the list of stories, then the list of comments in each story, add up those replies, and calculate a story's popularity score?
public void CalculateStoryPopularityScore(List<Story> stories)
{
if (stories == null) throw new ArgumentException("Stories can't be null");
double CommentScore;
double InsightfulVoteScore;
double UsefulVoteScore;
double PopularityScore;
double ViewCount;
foreach (var story in stories)
{
CommentScore = story.Comments.Count() * 4;
InsightfulVoteScore = story.InsightFulVoteCount * 3;
UsefulVoteScore = story.UsefulVoteCount * 2;
ViewCount = story.ViewCount;
PopularityScore = CommentScore + InsightfulVoteScore + UsefulVoteScore + ViewCount;
story.PopularityScore = PopularityScore;
}
}
Use SelectMany
var commentCount = story.Comments.Count();
// count all replies to all comments for a story
var replyCountSum = story.Comments
.SelectMany(c => c.Replies)
.Count();
Apply to a collection of stories:
stories.Select(s => new
{
Story = s,
CommentCount = s.Comments.Count(),
ReplyCount = s.Comments.SelectMany(c => c.Replies).Count(),
});
Unless I'm missing something, all the scores you're calculating with a separate method can instead be written as a public read-only (calculated) property of the Story class. The reply count can be obtained by using SelectMany (which is used to flatten lists of lists into a single list) and then getting the Count property:
public class Story
{
public List<Comment> Comments { get; set; }
public int InsightFulVoteCount { get; set; }
public int UsefulVoteCount { get; set; }
public int ViewCount { get; set; }
public int PopularityScore
{
get
{
return
(Comments?.Count ?? 0) * 4 +
(Comments?.SelectMany(comment => comment.Replies).Count() ?? 0) * 4 +
InsightFulVoteCount * 3 +
UsefulVoteCount * 2 +
ViewCount;
}
}
}
public class Comment
{
public List<string> Replies { get; set; }
}
In case you're not familiar with the null-conditional operator (?.), it returns null if the left operand (the object) is null before accessing the right operand (property or method of the object). If the left side is not null, then the property/method value is returned.
Then the null-coalescing operator (??) evaluates the left operand (which is the result of the property or method access) and, if it's null, it returns the right operand ('0' in our case).
Basically this simplifies the code. You don't have to do:
var score = 0;
if (Comments != null) score = Comments.Count;
You can just do:
var score = Comments?.Count ?? 0;
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am new to C# and not so expert in string manipulation. I have a string which represents the position of an object in diagram. The string contains integer values which I want to change for each object.
Example
String Position = "l=50; r=190; t=-430; b=-480";
I want to modify this string to
String Position = "l=50; r=190; t=-505; b=-555";
So if you notice t = -430 is changed to t = -505 and b = -480 to b = -555 which means an increment of -75 in both top and bottom
How can I do this ?
Thanks
If you want to easily populate or modify the values in your input string, you can use string.Format(), like this:
Position = string.Format("l={0}; r={1}; t={2}; b={3}", 50, 190, -430, -480);
You can extract the values of an existing input string using regular expressions, like this:
String Position = "l=50; r=190; t=-430; b=-480";
string pattern = #"^l=(\-{0,1}\d+); r=(\-{0,1}\d+); t=(\-{0,1}\d+); b=(\-{0,1}\d+)$";
var captGroups = Regex.Match(Position, pattern).Groups;
var l = captGroups[1];
var r = captGroups[2];
var t = captGroups[3];
var b = captGroups[4];
You need to parse your expression. Here is an example, in which the string is first splitted for ; and then each part is splitted by =:
var position = "l=50; r=190; t=-430; b=-480";
var parts = position.Split(';'); // split string into 4 parts
var assignments = new Dictionary<string, int>();
foreach (var part in parts)
{
var trimmedPart = part.Trim();
var assignmentParts = trimmedPart.Split('='); // split each part into variable and value part
var value = Int32.Parse(assignmentParts[1]); // convert string to integer value
assignments.Add(assignmentParts[0], value);
}
// change values
assignments["t"] = -505;
assignments["b"] = -555;
// build new string
var newPosition = String.Join("; ", assignments.Select(p => p.Key + "=" + p.Value));
Console.WriteLine(">> " + newPosition);
String Position = "l=50; r=190; t=-430; b=-480";
public void modifyPosition(int l, int r, int t, int b)
{
string[] parts = Position.Split(';');
int oldL = int.Parse(parts[0].Replace("l=","").Trim());
int oldR = int.Parse(parts[1].Replace("r=","").Trim());
int oldT = int.Parse(parts[2].Replace("t=","").Trim());
int oldB = int.Parse(parts[3].Replace("b=","").Trim());
Position = "l="+(oldL+l).ToString()+"; r="+(oldR+r).ToString()+
"; t="+(oldT+t).ToString()+"; b="+(oldB+b).ToString()+";";
}
There are a lot of ways to generalize and optimize that -- I'll leave those up to you...
To provide a further explanation to Jeroen van Langen's comment
You should parse the string into an object, change the values and reformat a string.
You can achieve this with a similar setup to this:
public class Position
{
public int l { get; set; }
public int r { get; set; }
public int t { get; set; }
public int b { get; set; }
public override string ToString()
{
return $"l = {l}, r = {r}, t = {t}, b = {b}";
}
}
(Change the access modifiers to suit your needs)
And work with the object (simply) as below:
public void UpdatePostion()
{
// Create new position
Position pos = new Position
{
l = 50,
r = 190,
t = -430,
b = -480
};
Console.WriteLine($"Pos before change: {pos.ToString()}");
// change top and bottom value to accommodate increment
pos.t += -75;
pos.b += -75;
// Prove that they've been updated
Console.WriteLine($"Pos after change: {pos.ToString()}");
}
You don't usually want to manipulate data in string form unless the data is actually a string. In this case, you've really got a structure which is being represented as a string, so you should turn it into a structure first.
Based on the string you provided, this turns any string of a similar form into a dictionary.
var source = "l=50; r=190; t=-505; b=-555";
var components = source.Split(';').Select(x => x.Trim());
// components is now an enumerable of strings "l=50", "r=190" etc.
var pairs = components.Select(x => x.Split('=').ToArray());
// pairs is now an enumerable of arrays of strings ["l", "50"], ["r", "190"] etc;
var dictionary = pairs.ToDictionary(x => x[0], x => x[1]);
// dictionary now contains r => 190, l => 50 etc.
Of course, you want to play with actual numbers, so you want to convert the values to integers really, so swap that last line out:
var dictionary = pairs.ToDictionary(x => x[0], x => Convert.ToInt32(x[1]));
Now you have a Dictionary which you can manipulate:
dictionary["t"] = dictionary["t"] - 75;
For example. Or you could use this as the basis of populating an object with appropriate properties and manipulate those.
struct Position {
public int L { get; set; }
public int R { get; set; }
public int T { get; set; }
public int B { get; set; }
}
Of course you might want to change it back into a string when you're done manipulating it:
var newString = string.Join("; ", dictionary.Select(x => $"{x.Key}={x.Value}"));
Although if you're planning to manipulate it a lot you should keep the parsed version around for as long as possible, as the parse/render cycle could be a performance issue if you do a lot of it.
Always store your data in appropriate types. Write new classes, write new structs, they don't have to be complicated but the more you can give names and shapes to your data the easier it is to manipulate it and to ensure that you manipulated it correctly.
This is all very simplistic of course - there's no error handling, such as if the value part isn't a valid Int32 (Convert.ToInt32 will throw an exception) or if there are duplicate keys (ToDictionary will throw an exception) etc. But hopefully it gives you some idea where to go.
It's also not the only way you could parse this kind of string, but it's fairly straightforward to follow and shouldn't be too slow, although it's not going to be very memory-efficient on a large input string.
Check this fiddle here.
public class MyObject
{
public int r {get;set;}
public int l {get;set;}
public int t {get;set;}
public int b {get;set;}
public MyObject()
{
}
public void ParseFromString(string val)
{
string[] splitVal = val.Split(';');
int intVal ;
if(!int.TryParse(splitVal[0].Replace("l=","").Trim(), out intVal))
intVal = 0;
this.l = intVal;
if(!int.TryParse(splitVal[1].Replace("r=","").Trim(), out intVal))
intVal = 0;
this.r = intVal;
if(!int.TryParse(splitVal[2].Replace("t=","").Trim(), out intVal))
intVal = 0;
this.t = intVal;
if(!int.TryParse(splitVal[3].Replace("b=","").Trim(), out intVal))
intVal = 0;
this.b = intVal;
}
public string ParseToString()
{
return "l=" + this.l + "; r=" + this.r + "; t=" + this.t + "; b=" + this.b + "";
}
}
So, building on one of the comments suggestions about parsing into an object - you could represent your Position object as follows:
public class Position
{
public int Left { get; set; }
public int Right { get; set; }
public int Top { get; set; }
public int Bottom { get; set; }
public Position(string str)
{
int[] values = str.Split(new[] { "; " }, StringSplitOptions.None)
.Select(s => Convert.ToInt32(s.Substring(s.IndexOf('=') +1))).ToArray();
Left = values[0];
Right = values[1];
Top = values[2];
Bottom = values[3];
}
public override string ToString()
{
return string.Join("; ", Left, Right, Top, Bottom);
}
}
You could then use this object to serialize/deserialize your string to and from an object as follows:
string intputString = "l=50; r=190; t=-430; b=-480";
Position p = new Position(intputString);
p.Top -= 75;
p.Bottom -= 75;
string outputString = p.ToString();`
The only thing of any real complexity is turning your string into the object values. To do this we can delimit the string into each individual value by splitting the string on "; ". Each value can then be turned into an integer by taking the substring of each element after the equals character ("l=50" becomes "50") and converting it into an integer.
I think the best approach would be to use a regex in this case:
var position = "l=50; r=190; t=-430; b=-480";
var match = Regex.Match(position, #"l=(-?\d+); r=(-?\d+); t=(-?\d+); b=(-?\d+)");
if (!match.Success)
throw new Exception("invalid data");
var l = Convert.ToInt32(match.Groups[1].Value);
var r = Convert.ToInt32(match.Groups[2].Value);
var t = Convert.ToInt32(match.Groups[3].Value);
var b = Convert.ToInt32(match.Groups[4].Value);
// modify the values as you like, for example: t -= 75; b -=75;
var newPosition = $"l={l}; r={r}; t={t}; b={b}";
I think a simple method can do the trick:
public static string ModifyPositions(string positionsInput, int displacement)
{
string input = "l=50; r=190; t=-430; b=-480";
var pattern = #"(t|b)=(-?\d+)";
var regex = new Regex(pattern);
var matches = regex.Matches(input);
foreach (Match match in matches)
{
input = input.Replace(match.Groups[2].Value, (int.Parse(match.Groups[2].Value) + displacement).ToString());
}
return input;
}
In your case displacement is -75, positionsInput is "l=50; r=190; t=-430; b=-480"
You can create a Position structure, like this:
public struct Position
{
public int Left { get; private set; }
public int Top { get; private set; }
public int Right { get; private set; }
public int Bottom { get; private set; }
public Position(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public Position(string positionText)
{
string pattern = #"(?=l=(?<left>[\d\-]+))|(?=t=(?<top>[\d\-]+))|(?=r=(?<right>[\d\-]+))|(?=b=(?<bottom>[\d\-]+))";
Match match = Regex.Match(positionText, pattern);
int left = Convert.ToInt32(match.Groups["left"].Value);
int top = Convert.ToInt32(match.Groups["top"].Value);
int right = Convert.ToInt32(match.Groups["right"].Value);
int bottom = Convert.ToInt32(match.Groups["bottom"].Value);
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public Position Move(int leftDelta = 0, int topDelta=0, int rightDelta=0, int bottomDelta = 0)
{
return new Position(Left + leftDelta, Top + topDelta, Right + rightDelta, Bottom + bottomDelta);
}
public override string ToString()
{
return string.Format("l={0}; r={1}; t={2}; b={3}", Left, Right, Top, Bottom);
}
}
Then you can use it like this:
Position oldPosition = new Position("l=50; r=190; t=-430; b=-480");
Position newPosition = oldPosition.Move(topDelta: -75, bottomDelta: -75);
string newPositionText = newPosition.ToString();
I have a textile contains big amount of data ,first thing is i have to filter Leaf cell data which is scattered over there and here.For this first line i filtered is line beginning with ADD GCELL which contains primary data,next what i have to do is i have to get the related data from the same text file by using CELLID coming in the same ADD GCELL line.Related datas are coming in the line beggining with ADD GTRX and datas are FREQ , TRXNO , ISMAINBCCH ,.in nutshell CELLID is the common value for both line ADD GCELL and ADD GTRX. I have done few coding in c# , but i got stuck somewhere
Here is part of text file
...........................
...........................
ADD GCELL:CELLID=13, CELLNAME="NR_0702_07021_G1_A", MCC="424", MNC="02", LAC=6112, CI=7021, NCC=6, BCC=0, EXTTP=Normal_cell, IUOTP=Concentric_cell, ENIUO=ON, DBFREQBCCHIUO=Extra, FLEXMAIO=OFF, CSVSP=3, CSDSP=5, PSHPSP=4, PSLPSVP=6, BSPBCCHBLKS=1, BSPAGBLKSRES=4, BSPRACHBLKS=1, TYPE=GSM900_DCS1800, OPNAME="Tester", VIPCELL=NO
..............................
ADD GTRX:TRXID=11140, TRXNAME="T_RAK_JaziratHamra_G_702_7021_A-0", FREQ=99, TRXNO=0, CELLID=13, IDTYPE=BYID, ISMAINBCCH=YES, ISTMPTRX=NO, GTRXGROUPID=80;
Code i have done is
using (StreamReader sr = File.OpenText(filename))
{
while ((s = sr.ReadLine()) != null)
{
if (s.Contains("ADD GCELL:"))
{
s = s.Replace("ADD GCELL:", "");
string[] items = s.Split(',');
foreach (string str in items)
{
string[] str1 = str.Split('=');
if (str1[0] == "CELLID")
{
cellidnew = str1[1];
}
string fieldname = str1[0];
string value = str1[1].Replace(";", string.Empty).Replace("\"", string.Empty);
}
Getgtrxvalues(filename, ref cellname, ref cellidnew, ref Frequency, ref TRXNO ,ref ISMAINBCCH);
}
}
}
private static void Getgtrxvalues(string filename, ref string cellname, ref string cellid, ref int Frequency, ref int TRXNO ,ref bool ISMAINBCCH)
{
using (StreamReader sr = File.OpenText(filename))
{
while ((s = sr.ReadLine()) != null)
{
if (s.Contains("ADD GTRX:"))
{
try
{
}
}
}
}
UPDATE
Everything working fine except one more condition i have to satisfy.Here for for ADD Gtrx: i am taking all values including Freq when ISMAINBCCH=YES ,but at the same time ISMAINBCCH=NO there are values for Freq which i have to get as comma seperated values.For example Like here First i will take FREQ where CELLID = 639(dynamic one anything can happen) and ISMAINBCCH=YES,that i have done now next task is i have to contenate FREQ values in a comma seperated way where CELLID=639 and ISMAINBCCH=NO, so here the output i want is 24,28,67 .How to achieve this one
lines are
ADD GTRX:TRXID=0, TRXNAME="M_RAK_JeerExch_G_1879_18791_A-0", FREQ=81, TRXNO=0, CELLID=639, IDTYPE=BYID, ISMAINBCCH=YES, ISTMPTRX=NO, GTRXGROUPID=2556;
ADD GTRX:TRXID=1, TRXNAME="M_RAK_JeerExch_G_1879_18791_A-1", FREQ=24, TRXNO=1, CELLID=639, IDTYPE=BYID, ISMAINBCCH=NO, ISTMPTRX=NO, GTRXGROUPID=2556;
ADD GTRX:TRXID=5, TRXNAME="M_RAK_JeerExch_G_1879_18791_A-2", FREQ=28, TRXNO=2, CELLID=639, IDTYPE=BYID, ISMAINBCCH=NO, ISTMPTRX=NO, GTRXGROUPID=2556;
ADD GTRX:TRXID=6, TRXNAME="M_RAK_JeerExch_G_1879_18791_A-3", FREQ=67, TRXNO=3, CELLID=639, IDTYPE=BYID, ISMAINBCCH=NO, ISTMPTRX=NO, GTRXGROUPID=2556;
UPDATE
Finally i did it like shown below code
i created one more property DEFINED_TCH_FRQ = null for getting concatenated string.But the problem is it is very slow .I am iterating text file two times ,first time is sr.readline() and second is for getting concatenated string by File.Readline(this aslo previously i used File.Readalllines and got out of memory exception)
List<int> intarr = new List<int>();
intarr.Clear();
var gtrx = new Gtrx
{
CellId = int.Parse(PullValue(s, "CELLID")),
Freq = int.Parse(PullValue(s, "FREQ")),
TrxNo = int.Parse(PullValue(s, "TRXNO")),
IsMainBcch = PullValue(s, "ISMAINBCCH").ToUpper() == "YES",
Commabcch = new List<string> { PullValue(s, "ISMAINBCCH") },
DEFINED_TCH_FRQ = null,
TrxName = PullValue(s, "TRXNAME"),
};
if (!intarr.Contains(gtrx.CellId))
{
if (!_dictionary.ContainsKey(gtrx.CellId))
{
// No GCell record for this id. Do something!
continue;
}
intarr.Add(gtrx.CellId);
string results = string.Empty;
var result = String.Join(",",
from ss in File.ReadLines(filename)
where ss.Contains("ADD GTRX:")
where int.Parse(PullValue(ss, "CELLID")) == gtrx.CellId
where PullValue(ss, "ISMAINBCCH").ToUpper() != "YES"
select int.Parse(PullValue(ss, "FREQ")));
results = result;
var gtrxnew = new Gtrx
{
DEFINED_TCH_FRQ = results
};
_dictionary[gtrx.CellId].Gtrx = gtrx;
UPDATE
Finally i did it like first i saved lines starting with ADD GTRX in to an array by using File.Readalllines and then used only that array to get concatenated string instead of storing entire text file and got some performance improvement.Now my question is if i convert my Text files each contain hundreds of thousands of lines in to xml and then retrieve data from xml file, will it make any performance improvement? if i use datatable and dataset rather than classes here will it make any performance improvement?
Assuming the data is consistent and I'm also assuming the GCells will come before GTrx line (since GTrx is referencing the id of the GCell), then you could create a simple parser for doing this and store the values in a dictionary.
First thing to do is create a class to hold the Gtrx data and the GCell data. Keep in mind that I am just grabbing a subset of the data. You can add to this if you need more fields:
private class Gtrx
{
public int Freq { get; set; }
public int TrxNo { get; set; }
public string TrxName { get; set; }
public int CellId { get; set; }
public bool IsMainBcch { get; set; }
}
private class Gcell
{
public int CellId { get; set; }
public string CellName { get; set; }
public string Mcc { get; set; }
public int Lac { get; set; }
public int Ci { get; set; }
}
In addition to these classes, we will also need a class to "link" these two classes together:
private class GcellGtrx
{
public Gcell Gcell { get; set; }
public Gtrx Gtrx { get; set; }
}
Now we can build a simple parser:
private readonly Dictionary<int, GcellGtrx> _dictionary = new Dictionary<int, GcellGtrx>();
string data = "ADD GCELL:CELLID=13, CELLNAME=\"NR_0702_07021_G1_A\", MCC=\"424\", MNC=\"02\", LAC=6112, CI=7021, NCC=6, BCC=0, EXTTP=Normal_cell, IUOTP=Concentric_cell, ENIUO=ON, DBFREQBCCHIUO=Extra, FLEXMAIO=OFF, CSVSP=3, CSDSP=5, PSHPSP=4, PSLPSVP=6, BSPBCCHBLKS=1, BSPAGBLKSRES=4, BSPRACHBLKS=1, TYPE=GSM900_DCS1800, OPNAME=\"Tester\", VIPCELL=NO" + Environment.NewLine;
data = data + "ADD GTRX:TRXID=11140, TRXNAME=\"T_RAK_JaziratHamra_G_702_7021_A-0\", FREQ=99, TRXNO=0, CELLID=13, IDTYPE=BYID, ISMAINBCCH=YES, ISTMPTRX=NO, GTRXGROUPID=80;" + Environment.NewLine;
using (var sr = new StringReader(data))
{
string line = sr.ReadLine();
while (line != null)
{
line = line.Trim();
if (line.StartsWith("ADD GCELL:"))
{
var gcell = new Gcell
{
CellId = int.Parse(PullValue(line, "CELLID")),
CellName = PullValue(line, "CELLNAME"),
Ci = int.Parse(PullValue(line, "CI")),
Lac = int.Parse(PullValue(line, "LAC")),
Mcc = PullValue(line, "MCC")
};
var gcellGtrx = new GcellGtrx();
gcellGtrx.Gcell = gcell;
_dictionary.Add(gcell.CellId, gcellGtrx);
}
if (line.StartsWith("ADD GTRX:"))
{
var gtrx = new Gtrx
{
CellId = int.Parse(PullValue(line, "CELLID")),
Freq = int.Parse(PullValue(line, "FREQ")),
TrxNo = int.Parse(PullValue(line, "TRXNO")),
IsMainBcch = PullValue(line, "ISMAINBCCH").ToUpper() == "YES",
TrxName = PullValue(line, "TRXNAME")
};
if (!_dictionary.ContainsKey(gtrx.CellId))
{
// No GCell record for this id. Do something!
continue;
}
_dictionary[gtrx.CellId].Gtrx = gtrx;
}
line = sr.ReadLine();
}
}
// Now you can pull your data using a CellId:
// GcellGtrx cell13 = _dictionary[13];
//
// Or you could iterate through each one:
// foreach (KeyValuePair<int, GcellGtrx> kvp in _dictionary)
// {
// int key = kvp.Key;
// GcellGtrx gCellGtrxdata = kvp.Value;
// // Do Stuff
// }
And finally, we need to define a simple helper method:
private string PullValue(string line, string key)
{
key = key + "=";
int ndx = line.IndexOf(key, 0, StringComparison.InvariantCultureIgnoreCase);
if (ndx >= 0)
{
int ndx2 = line.IndexOf(",", ndx, StringComparison.InvariantCultureIgnoreCase);
if (ndx2 == -1)
ndx2 = line.Length - 1;
return line.Substring(ndx + key.Length, ndx2 - ndx - key.Length).Trim('"').Trim();
}
return "";
}
That should do it! See if that doesn't work for you. Keep in mind that this is very basic. You'd probably want to handle some possible errors (such as the key not existing, etc).
You didn't specify what exactly is going wrong, but my guess is that the problem you are having is caused by your split:
string[] str1 = str.Split('=');
This split causes your strings to be " CELLID" and "13" (from your file example). Notice the space in front of "CELLID". This causes the following code to never pass:
if (str1[0] == "CELLID")
You could change it to:
if (str1[0].Trim() == "CELLID")
it might work.
Just quite confused with the piece of code below.
I have class like below
public class Counter
{
public Double NormalCounter { get; set; }
public Double SATCounter { get; set; }
public Double SUNCounter { get; set; }
}
in my main class i have method to do some calculation to fill the counter
Counter CountHrs = GetBookedHours(resourceBkg, PrevEvent);
var lstExpResult = new List<string> {CountHrs.NormalCounter.ToString(),
CountHrs.SATCounter.ToString(),
CountHrs.SUNCounter.ToString()};
UpdateBooking(bookingSesid, lstExpResult);
Just assume i have the value like below in the counter
NormalCounter =4
SATCounter=10
SUNCounter=6
am trying to add in to string list and update the database.is that the right way to do ? or any other options i have please.
my update booking method is below to give clear idea.
public static bool UpdateBooking(string BookingSesid,List<string>HoursByRate)
{
SchedwinEntities db = new SchedwinEntities();
string value = string.Empty;
for (int i = 0; i < 5; i++)
{
string str = " ";
if (i < HoursByRate.Count())
{
str = HoursByRate[i];
value += str + ((char)13).ToString() + ((char)10).ToString();
}
}
var query =
from SEVTs in db.SEVTs
where
SEVTs.SESID.Trim() == BookingSesid//SESID
select SEVTs;
foreach (var SEVTs in query)
{
SEVTs.USER3 = value;//value
}
try
{
db.SaveChanges();
return true;
}
catch (UpdateException ex)
{
return false;
}
}
Rather than passing a list of strings that represent doubles, you should pass a list of key-value pairs, construct a parametersized statement from them, and use the list of key-value-pairs to bind parameter values, like this:
class SqlParamBinding {
string Name {get;set;}
object Value {get;set;}
}
var lstExpResult = new List<SqlParamBinding> {
new SqlParamBinding { Name = "NormalCounter", Value = CountHrs.NormalCounter}
, new SqlParamBinding { Name = "SATCounter", Value = CountHrs.SATCounter}
, new SqlParamBinding { Name = "SUNCounter", Value = CountHrs.SUNCounter}
};
UpdateBooking(bookingSesid, lstExpResult);
Now that lstExpResult separates names from values, your UpdateBooking code could format the SQL expression as
WHERE NormalCounter=#NormalCounter AND SATCounter=#SATCounter AND ...
and then bind #NormalCounter, #SATCounter, and #SUNCounter to the values passed in the lstExpResult list.
If it is going to list of Counter classes then have List instead of List. Looks like you might be having bunch of Counter objects that might be getting updated or sent to the database.
Counter c = new Counter();
c. NormalCounter = 4
c.SATCounter = 10
c.SunCounter = 10
List<Counter> listCounter = new List<Counter>
listCounter.Add(c);
Code is more maintainable and readable.
If you are sending one object at a time, then no need of list at all. You can pass in the counter object to your UpdateMethod and parse it while updating the database.