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 created a public struct Values that has public string value1and public string value2.
public struct Values
{
public string header;
public string type;
}
My dictionary:
var myDictionary = new Dictionary<int, Values>();
Question: How do I add two values for each key?
while (true)
{
for (int i = 0; i < end i++)
{
myDictionary.Add(i, value1 , value2);
}
}
If you want to generate dictionary, you can try using Linq:
var myDictionary = Enumerable
.Range(0, end)
.Select(i => new {
key = i,
value = new Values() {
header = HeaderFromIndex(i), //TODO: implement this
type = TypeFromIndex(i) //TODO: implement this
}})
.ToDictionary(item => item.key, item => item.value);
In case you want to add items into existing dictionary:
for (int i = 0; i < end; ++i)
myDictionary.Add(i, new Values() {
header = HeaderFromIndex(i), //TODO: implement this
type = TypeFromIndex(i) //TODO: implement this
});
Please notice, that in any case dictionary holds pairs: {key, value}; so if you want to have two items as values for the corresponding key, you have to organize the values into a class new Values() {header = ..., type = ...} in your case
If I get the question correctly, you have to initialize a Values object and than add this one to your dictionary. Like this:
while (true) {
for (int i = 0; i < end i++) {
Values tmp_values;
tmp_values.header = "blabla";
tmp_values.type = "blabla type";
myDictionary.Add(i, tmp_values);
}
}
I need to sort a list of scene numbers which are in fact a list of string and contain numbers and letters.
this is the list
101-11
102-1
101-10
101-8
103-10
101-8A
101-9
103-4
103-4B
I've made following a Comparer
public class SceneComparer : IComparer
{
private readonly Regex sceneRegEx = new Regex(#"(\D*)(\w*)", RegexOptions.Compiled);
public int Compare(object x, object y)
{
Scene sceneX = x as Scene;
Scene sceneY = y as Scene;
var firstSceneMatch = this.sceneRegEx.Match(sceneX.SceneNumber);
var firstSceneNumeric = Convert.ToInt32(firstSceneMatch.Groups[1].Value);
var firstSceneAlpha = firstSceneMatch.Groups[2].Value;
var secondSceneMatch = this.sceneRegEx.Match(sceneY.SceneNumber);
var secondSceneNumeric = Convert.ToInt32(secondSceneMatch.Groups[1].Value);
var secondSceneAlpha = secondSceneMatch.Groups[2].Value;
if (firstSceneNumeric < secondSceneNumeric)
{
return -1;
}
if (firstSceneNumeric > secondSceneNumeric)
{
return 1;
}
return string.CompareOrdinal(firstSceneAlpha, secondSceneAlpha);
}
}
Which gives me following result
101-8
101-8A
101-9
102-1
103-4
103-4B
101-10
101-11
103-10
It looks like it's only sorting the first number before the dash and the alphanumeric but it doesn't sort the number after the dash to get following desired result.
101-8
101-8A
101-9
101-10
101-11
102-1
103-4
103-4B
103-10
Any idea on how to get the desired result.
You have to Split the string and extract numbers in it to request sort on those fields.
// Assuming you have this...
List<string> list = new List<string>()
{
"101-11",
"102-1",
"101-10",
"101-8",
"103-10",
"101-8A",
"101-9",
"103-4",
"103-4B"
};
// You could do this.
var result = list.Select(x=>
{
var splits = x.Split('-');
return new
{
first = int.Parse(splits[0]),
second = int.Parse(Regex.Match(splits[1], #"\d+").Value),
item =x
};
})
.OrderBy(x=>x.first)
.ThenBy(x=>x.second)
.Select(x=>x.item)
.ToList();
Check this Demo
You are very close. Use Matches instead of Match.
Also, correct your Regex as \D captures non-digit characters.
public class SceneComparer : IComparer
{
private readonly Regex sceneRegEx = new Regex(#"(\d+)(\w+)?", RegexOptions.Compiled);
public int Compare(object x, object y)
{
Scene sceneX = x as Scene;
Scene sceneY = y as Scene;
var firstSceneMatches = this.sceneRegEx.Matches(sceneX.SceneNumber);
var secondSceneMatches = this.sceneRegEx.Matches(sceneY.SceneNumber);
var matchesCount = Math.Min(firstSceneMatches.Count, secondSceneMatches.Count);
for (var i = 0; i < matchesCount; i++)
{
var firstSceneMatch = firstSceneMatches[i];
var secondSceneMatch = secondSceneMatches[i];
var firstSceneNumeric = Convert.ToInt32(firstSceneMatch.Groups[1].Value);
var secondSceneNumeric = Convert.ToInt32(secondSceneMatch.Groups[1].Value);
if (firstSceneNumeric != secondSceneNumeric)
{
return firstSceneNumeric - secondSceneNumeric;
}
var firstSceneAlpha = firstSceneMatch.Groups[2].Value;
var secondSceneAlpha = secondSceneMatch.Groups[2].Value;
if (firstSceneAlpha != secondSceneAlpha)
{
return string.CompareOrdinal(firstSceneAlpha, secondSceneAlpha);
}
}
return firstSceneMatches.Count - secondSceneMatches.Count;
}
}
i have this code:
List<T> apps = getApps();
List<int> ids;
List<SelectListItem> dropdown = apps.ConvertAll(c => new SelectListItem
{
Selected = ids.Contains(c.Id),
Text = c.Name,
Value = c.Id.ToString()
}).ToList();
ids.Contains
seems to always return false even though the numbers do match
any ideas?
If you just need a true/false result
bool isInList = intList.IndexOf(intVariable) != -1;
if the intVariable does not exist in the List it will return -1
As long as your list is initialized with values and that value actually exists in the list, then Contains should return true.
I tried the following:
var list = new List<int> {1,2,3,4,5};
var intVar = 4;
var exists = list.Contains(intVar);
And exists is indeed set to true.
Here is a extension method, this allows coding like the SQL IN command.
public static bool In<T>(this T o, params T[] values)
{
if (values == null) return false;
return values.Contains(o);
}
public static bool In<T>(this T o, IEnumerable<T> values)
{
if (values == null) return false;
return values.Contains(o);
}
This allows stuff like that:
List<int> ints = new List<int>( new[] {1,5,7});
int i = 5;
bool isIn = i.In(ints);
Or:
int i = 5;
bool isIn = i.In(1,2,3,4,5);
The way you did is correct. It works fine with that code: x is true.
probably you made a mistake somewhere else.
List<int> ints = new List<int>( new[] {1,5,7}); // 1
List<int> intlist=new List<int>() { 0,2,3,4,1}; // 2
var i = 5;
var x = ints.Contains(i); // return true or false
The best of code and complete is here:
NumbersList.Exists(p => p.Equals(Input)
Use:
List<int> NumbersList = new List<int>();
private void button1_Click(object sender, EventArgs e)
{
int Input = Convert.ToInt32(textBox1.Text);
if (!NumbersList.Exists(p => p.Equals(Input)))
{
NumbersList.Add(Input);
}
else
{
MessageBox.Show("The number entered is in the list","Error");
}
}
bool vExist = false;
int vSelectValue = 1;
List<int> vList = new List<int>();
vList.Add(1);
vList.Add(2);
IEnumerable vRes = (from n in vListwhere n == vSelectValue);
if (vRes.Count > 0) {
vExist = true;
}
You should be referencing Selected not ids.Contains as the last line.
I just realized this is a formatting issue, from the OP. Regardless you should be referencing the value in Selected. I recommend adding some Console.WriteLine calls to see exactly what is being printed out on each line and also what each value is.
After your update:
ids is an empty list, how is this not throwing a NullReferenceException? As it was never initialized in that code block
string name= "abc";
IList<string> strList = new List<string>() { "abc", "def", "ghi", "jkl", "mno" };
if (strList.Contains(name))
{
Console.WriteLine("Got It");
}
///////////////// OR ////////////////////////
IList<int> num = new List<int>();
num.Add(10);
num.Add(20);
num.Add(30);
num.Add(40);
Console.WriteLine(num.Count); // to count the total numbers in the list
if(num.Contains(20)) {
Console.WriteLine("Got It"); // if condition to find the number from list
}
I have a text box field inputs 123,145,125 I to separate this field into an array of integers. And validate this field true or false if everything is parsed right.
CODE:
private bool chkID(out int[] val)
{
char[] delimiters = new char[] { ',' };
string[] strSplit = iconeID.Text.Split(delimiters);
int[] intArr = null;
foreach (string s in strSplit) //splits the new parsed characters
{
int tmp;
tmp = 0;
if (Int32.TryParse(s, out tmp))
{
if (intArr == null)
{
intArr = new int[1];
}
else
{
Array.Resize(ref intArr, intArr.Length + 1);
}
intArr[intArr.Length - 1] = tmp;
}
if (Int32.TryParse(iconeID.Text, out tmp))
{
iconeID.BorderColor = Color.Empty;
iconeID.BorderWidth = Unit.Empty;
tmp = int.Parse(iconeID.Text);
val = new int[1];
val[0] = tmp;
return true;
}
}
val = null;
ID.BorderColor = Color.Red;
ID.BorderWidth = 2;
return false;
}
//new Code:
private bool chkID(out int[] val) //bool satus for checkID function
{
string[] split = srtID.Text.Split(new char[1] {','});
List numbers = new List();
int parsed;
bool isOk = true;
foreach( string n in split){
if(Int32.TryParse( n , out parsed))
numbers.Add(parsed);
else
isOk = false;
}
if (isOk){
strID.BorderColor=Color.Empty;
strID.BorderWidth=Unit.Empty;
return true;
} else{
strID.BorderColor=Color.Red;
strID.BorderWidth=2;
return false;
}
return numbers.ToArray();
}
The given function seems to do too much. Here's one that answers the question implied by your title:
//int[] x = SplitStringIntoInts("1,2,3, 4, 5");
static int[] SplitStringIntoInts(string list)
{
string[] split = list.Split(new char[1] { ',' });
List<int> numbers = new List<int>();
int parsed;
foreach (string n in split)
{
if (int.TryParse(n, out parsed))
numbers.Add(parsed);
}
return numbers.ToArray();
}
EDIT (based on your comment on the question)
You've defined the three things this function needs to do. Now you just need to create methods for each. Below are my guesses for how you could implement them.
int[] ValidateIDs(int[] allIDs)
{
List<int> validIDs = new List<int>(allIDs);
//remove invalid IDs
return validIDs.ToArray();
}
void DownloadXmlData(int[] ids)
{
...
}
Now you just execute your new functions:
void CheckIconeID(string ids)
{
int[] allIDs = SplitStringIntoInts(ids);
int[] validIDs = ValidateIDs(allIDs);
DownloadXmlData(validIDs);
}
I really wanted to comment on #Austin Salonen's answer, but it didn't fit. It is a great answer for the question asked, but i wanted to expand the discussion a bit more generally on csv/int conversion part.
It's small point, not worth much debate but I would consider swapping the foreach loop for a plain for loop. You'll likely end up with simpler IL (read faster). See (http://www.codeproject.com/KB/cs/foreach.aspx, http://msdn.microsoft.com/en-us/library/ms973839.aspx [Use For Loops for String Iteration—version 1]).
I would create two methods -- one that is safe and uses TryParse and only adds the "good" values, another that is not as safe, but faster.
Proposed "safe" function (with overload in case you don't want to know the bad values)...
public static int[] SplitAsIntSafe (this string csvString) {
List<string> badVals;
return SplitAsIntSafe(csvString, ',', out badVals);
}
public static int[] SplitAsIntSafe (this string delimitedString, char splitChar, out List<string> badVals) {
int parsed;
string[] split = delimitedString.Split(new char[1] { ',' });
List<int> numbers = new List<int>();
badVals = new List<string>();
for (var i = 0; i < split.Length; i++) {
if (int.TryParse(split[i], out parsed)) {
numbers.Add(parsed);
} else {
badVals.Add(split[i]);
}
}
return numbers.ToArray();
}
Proposed "fast" function ....
public static int[] SplitAsIntFast (this string delimitedString, char splitChar) {
string[] strArray = delimitedString.Split(splitChar);
int[] intArray = new int[strArray.Length];
if(delimitedString == null) {
return new int[0];
}
for (var i = 0; i < strArray.Length; i++) {
intArray[i] = int.Parse(strArray[i]);
}
return intArray;
}
Anyway, hope this helps someone.
It might be worth your while to check out this FileHelper and also CSV Reader
Hope they will help you...
Take care,
Tom
There is a good free library for parsing CSV files: FileHelpers
using FileHelpers;
// First declare the record class
[Delimitedrecord(";")]
public class SampleType
{
public string Field1;
public int Field2;
}
public void ReadExample()
{
FileHelperEngine engine = new FileHelperEngine(typeof(SampleType));
SampleType[] records;
records = (SampleType[]) engine.ReadFile("source.txt");
// Now "records" array contains all the records in the
// sourcefile and can be acceded like this:
int sum = records[0].Field2 + records[1].Field2;
}
public bool ParseAndCheck(string source,
out IList<int> goodItems, out IList<string> badItems)
{
goodItems = new List<int>();
badItems = new List<string>();
foreach (string item in source.Split(','))
{
int temp;
if (int.TryParse(item, out temp))
goodItems.Add(temp);
else
badItems.Add(item);
}
return (badItems.Count < 1);
}
In .NET 2.0 you could write
string test = "123,14.5,125,151,1.55,477,777,888";
bool isParsingOk = true;
int[] results = Array.ConvertAll<string,int>(test.Split(','),
new Converter<string,int>(
delegate(string num)
{
int r;
isParsingOk &= int.TryParse(num, out r);
return r;
}));
This is simple and I think works pretty well. It only return valid numbers:
static int[] SplitStringIntoInts(string list)
{
int dummy;
return (from x in list.Split(',')
where int.TryParse(x.ToString(), out dummy)
select int.Parse(x.ToString())).ToArray();
}