I'm trying to build a dictionary of lists, but i'm reading in a string and need to make the lists name the string and add them to the dictionary as a key.
IE read in "hello"
Create the list with what was read in
List<string> (insert read string here ) = new List<string>();
Then add that lists name as the key to a dictionary.
Dictionary.Add(readstring, thatlist);
All I can find is a hard code implementation of this.
Turing.Add("S", S);
My goal: create a Universal Turing Machine, so I read in an input from a text file that is the next step that looks like this, (q0 a) -> q1 X R.
Then use the all the steps that I read in to end in a final state with this on the virtual tape "tape = XXYYZZBB"
I have the the pseudo code written for this but I just cant get the dictionary to work.
EDIT:
Adding some more info for less confusion.
Im given the start and the end state for the first 2 lines of the text file. Then im given the transitions.
q0 //start state
q5 //end state
q0 a q1 X R //transitions
Ive stripped the first two lines of input to give me 0 and 5 then have created a for loop to create a lists of each state.
for (int i = 0; i <= endState; i++)
{
List<string> i = new List<string>();
}
I then want to add each lists name as a key to the dictionary of lists I am creating.
Dictionary.Add(listname, thatlist);
I need help on implementing the code for the above as its giving errors.
It doesn't matter whether you create your list as
List<string> insertReadStringHere = new List<string>();
or
List<string> foo = new List<string>();
or even
List<string> shellBeComingRoundTheMountain = new List<string>();
What's important is that once you've done
MyDictionary.Add(theInputString, shellBeComingRoundTheMountain);
you can then access that particular list via
MyDictionary[theInputString]
wherether the initial list was "called" insertReadStringHere or foo or shellBeComingRoundTheMountain.
You don't even need to hold the list in a named variable like this. For example,
Console.WriteLine("Input a string to create a list:");
var createListName = Console.ReadLine();
// As long as they haven't used the string before...
MyDictionary.Add(createListName, new List<string>());
Console.WriteLine("Input a string to retrieve a list:");
var retrieveListName = Console.ReadLine();
// As long as they input the same string...
List<string> retrievedList = MyDictionary[retrieveListName];
Edit: If you want a certain number of lists, use a dictionarym apping from int to string, not string to string:
int maxNumberOfLists = 5; // or whatever you read from your text file.
Dictionary<int, List<string>> myLists =
new Dictionary<int, List<string>> (maxNumberOfLists);
for (int i = 1; i <= maxNumberOfLists; i++)
myLists[i] = new List<string>();
Then you can access your lists as e.g.
var firstList = myLists[1];
Ordinarily I'd recommend an array, but this gives you lists from 1 to 5 rather than from 0 to 4 and it seems that's what you want.
Related
I have some basic code that does what I want it to do, but I think it can be abbreviated / cleaned up. I am struggling a bit on how to do so, however.
The code reads as follows:
List<string> positions = new List<string>();
List<string> players = new List<string>();
foreach (string element in fractionedList)
{
positions.Add(element.Split(',')[2]);
positions.Add(element.Split(',')[3]);
positions.Add(element.Split(',')[4]);
players.Add(element.Split(',')[5]);
players.Add(element.Split(',')[6]);
players.Add(element.Split(',')[7]);
}
List<double> convertedPositions = positions.Select(x => double.Parse(x)).ToList();
List<double> convertedPlayers = playerss.Select(x => double.Parse(x)).ToList();
For reference, my fractionedList will look something like:
"string0,string1,string2,string3,string4,string5,string6,string7,string8,string9,string10,string11,string12",
"string0,string1,string2,string3,string4,string5,string6,string7,string8,string9,string10,string11,string12",
"string0,string1,string2,string3,string4,string5,string6,string7,string8,string9,string10,string11,string12",
"string0,string1,string2,string3,string4,string5,string6,string7,string8,string9,string10,string11,string12",
So I am trying to split each string instance of the List, get the next three elements, and then add them to a new List and then convert that List to a new List of doubles. I am wondering if there is a cleaner way to handle the Split method. Is there an equivalent to Take()? Also, can this all be done in one List creation, rather than creating a List of strings, creating a List of doubles?
The first thing I would change is to not split your string 6 times for no reason. Split it once and store the result in a variable.
With a little LINQ you could shorten your code:
List<double> positions = new List<double>();
List<double> players = new List<double>();
foreach (string element in fractionedList)
{
string[] elementSplit = element.Split(',');
positions.AddRange(elementSplit.Skip(2).Take(3).Select(x => double.Parse(x));
players.AddRange(elementSplit.Skip(5).Take(3).Select(x => double.Parse(x));
}
What my code does is split your element variable on , like you were doing (now only doing it once). Then using Linq's Take() and Skip() I am selecting the [2,3,4] and [5,6,7] indices and adding them to their respective lists (after parsing to double).
Keep in mind that this code will throw an exception if your string input is something that can not reasonably parse into a double. If you are certain that the input will always be good then this code should get you there the quickest.
This would perform the conversion inline, without the need to store in an initial string list
List<double> convertedPositions = new List<double>();
List<double> convertedPlayers = new List<double>();
foreach (string element in fractionedList)
{
var elements = element.Split(',');
convertedPositions.AddRange(elements.Skip(2).Take(3).Select(x=> Convert.ToDouble(x)));
convertedPositions.AddRange(elements.Skip(5).Take(3).Select(x => Convert.ToDouble(x));
}
I've been trying to figure out how to remove elements in my ArrayList where the value contains some text string.
My Array could look like this:
[0] "\"MAERSKA.CO\",N/A,N/A,N/A,N/A,N/A,N/A"
[1] "\"GEN.COABB.ST\",N/A,N/A,N/A,N/A,N/A,N/A"
[2] "\"ARCM.ST\",\"Arcam AB\",330.00,330.50,332.00,330.50,330.00"
And my ArrayList is created like this:
string stringToRemove = "NA";
ArrayList rows = new ArrayList(csvData.Replace("\r", "").Split('\n'));
So the question is how I delete all entries that contains "NA".
I have tried the RemoveAt or RemoveAll with several combinations of Contains but i cant seem to get the code right.
I do not want to make a new Array if it can be avoided.
Regards
Flemming
If you want to reduce your ArrayList before instantiate your variable, consider using LINQ:
ArrayList rows = new ArrayList(csvData.Replace("\r", "").Split('\n').Where(r => !r.Contains(stringToRemove)).ToList());
If you want to reduce your ArrayList after instantiation, you can try this:
for (int i = 0; i < rows.Count; i++)
{
var row = (string)rows[i];
if (row.Contains(stringToRemove))
{
rows.RemoveAt(i);
i--;
}
}
The following code creates a list as output containing all strings except "N/A":
var outputs = new List<string>();
foreach (var item in input)
{
var splitted = item.Split(',');
foreach (var splt in splitted)
{
if (splt != "N/A")
{
outputs.Add(splt);
}
}
}
The input is your array.
I have an ArrayList with multiples items on it, everyone of them is a String divided by commas "loglogs", the three first items are the localization (Destin, lat and long). I need to insert the Strings of these loglogs in buttons depending on its localization (based on that three parameters) in the button Tooltip or text programatically. I have all the button creation but I have to add the strings but there are more loglogs than buttons so...
I need to "filter" the ArrayList into another ArrayList, filter it depending on these three inital coordinates, I want to create another ArrayList but appending the strings that are identical in their three first elements of the arrayList. That way I will combine the "loglogs" into another "loglogsCondensed", with all the "localization" unique so I can add this part to my button and index creation.
foreach (String log in logslogs)
{
String[] colContent = log.Split(','); //splited the content with commas
Loglog log = new Loglog(); //Loglog is a class of logs with information in specific columns
log.Destin = colContent[0];
log.Lat = Convert.ToChar(colContent[1]);
log.Long = colContent[2];
log.Barcode = colContent[6];
log.Source = colContent[7];
log.SampleName = colContent[9];
AllLogs.Add(log);
I need to pass from logslogs with 1000 memebers to an ArrayList with less items, where the ones with the same location based on the three first items are appended as one item.
Suposse this is kind of easy if you know how to code properly (not my case). A thousand thanks only for read this out, even more to the people who try to help.
Best,
I have the solution!, probably is not going to win any contest of cleaneness but it does what I need!. I create an index to filter comparing the items depending of the three coordinates: Destin, Long and Lat. If they are the same I remove the last item and put the appended line in the last place and so on...
int c = 0; //Just to go the first time
//We create an index to compare the former with the "actual"
//log in every loop of the "foreach"
String IndiceDestin0 = string.Empty;
String IndiceLat0 = string.Empty;
String IndiceLong0 = string.Empty;
String IndiceDestin1;
String IndiceLat1;
String IndiceLong1;
foreach (String log in logslogs)
{
String[] LongContent = log.Split(',');
Loglog log = new Loglog();
log.Destin = LongContent[0];
log.Lat = Convert.ToChar(LongContent[1]);
log.Long = LongContent[2];
log.Barcode = LongContent[6];
log.Source = LongContent[7];
log.DestDestinBarcode = LongContent[8];
log.SampleName = LongContent[9];
AllLogs.Add(log);
//This only works once, the first time because we don't have a "former" data to compare we have to bypass the comparison
if (c == 0)
{
IndiceDestin0 = LongContent[0];
IndiceLat0 = LongContent[1];
IndiceLong0 = LongContent[2];
c++;
}
else
{
IndiceDestin1 = LongContent[0];
IndiceLat1 = LongContent[1];
IndiceLong1 = LongContent[2];
if (IndiceDestin0.Equals(IndiceDestin1) && IndiceLat0.Equals(IndiceLat1) && IndiceLong0.Equals(IndiceLong1))
{
int last = logsToButtons.Count - 1;
string oldLog = logsToButtons[last].ToString();
string appendedLog = oldLog + log;
//We remove the last "single" log to add the aggregated log
logsToButtons.RemoveAt(last);
logsToButtons.Add(appendedLog);
}
else
{
logsToButtons.Add(log);
}
IndiceDestin0 = IndiceDestin1;
IndiceLat0 = IndiceLat1;
IndiceLong0 = IndiceLong1;
c++;
}
}
I get to have a shorter version of the array but appending together the ones that have the same coordenates, thank you everybody for your help, I know is messy but it works!
Best,
Trying to read a csv file, and take the first word in the stream, throw it in to a dictionary while the following words get added to a list in that dictionary.
However, I find that (during debugging) when, inside my loop I decide to clear my list, all of the values it had added to the dictionary previously also get cleared. I guess I am mistaken in assuming it makes a copy of the list, it is actually just referencing that same list? Should I be creating a new list with every iteration? Code below:
public class TestScript : MonoBehaviour {
// Use this for initialization
void Start() {
Dictionary<string, List<string>> theDatabase = new Dictionary<string, List<string>>();
string word;
string delimStr = ",.:";
char[] delimiter = delimStr.ToCharArray();
List<string> theList = new List<string>();
using (StreamReader reader = new StreamReader("testComma.csv")) {
while (true) {
//Begin reading lines
string line = reader.ReadLine();
if (line == null) {
break;
}
//Begin splitting lines, adding to array.
string[] split2 = line.Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
//Loop to hold the first word in the stream
for(int i = 0; i <= 0; i++) {
word = split2[i];
//loop to hold the following words in to list.
for (int y = 1; y < split2.Length; y++) {
theList.Add(split2[y]);
}
//Add word/list combo in to the database
theDatabase.Add(word, theList);
//clear the list.
theList.Clear();
}
}
}
foreach (KeyValuePair<string, List<string>> pair in theDatabase) {
string keys;
List<string> values;
keys = pair.Key;
values = pair.Value;
print(keys + " = " + values);
}
}
}
The bottom foreach loop is just so I can see the results. Also, any critique is welcome in regards to how this is written, as I'm a beginner.
Yes, you're adding the same object to your dictionary.
You can just change :
theDatabase.Add(word, theList);
To :
theDatabase.Add(word, theList.ToList());
Method ToList() makes shallow copy of your List<T>
C# is pass by reference.
So, theList and the list in your Dictionary are the same object.
The simplest solution is to stop clearing your List and create a new one every time instead:
for(int i = 0; i <= 0; i++) {
List<string> theList = new List<string>(); // it is in a loop now
word = split2[i];
//loop to hold the following words in to list.
for (int y = 1; y < split2.Length; y++) {
theList.Add(split2[y]);
}
//Add word/list combo in to the database
theDatabase.Add(word, theList);
//clear the list.
//theList.Clear(); - not required anymore
}
It is more readable and clear solution: create a list, insert items, paste a list into the dictionary, continue the iteration.
It is also much more performant since there is no List clearing - List<T>.Clear() is a linear operation, which takes O(n) operations.
Yes, as everyone says, lists are reference types. You need to make a copy to avoid the .Clear() clearing all the lists.
You could always write your code like this:
void Start()
{
string delimStr = ",.:";
Dictionary<string, List<string>> theDatabase =
File
.ReadAllLines("testComma.csv")
.Select(line => line.Split(delimStr.ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.ToDictionary(x => x[0], x => x.Skip(1).ToList());
/* foreach here */
}
}
This doesn't have the problem with the list references.
So I tried some research, but I just don't know how to google this..
For example, I got a .db (works same as .txt for me) file, written like this:
DIRT: 3;
STONE: 6;
so far, i got a code that can put items in a comboBox like this:
DIRT,
STONE,
will put DIRT and STONE in the comboBox. This is the code I'm using for that:
string[] lineOfContents = System.IO.File.ReadAllLines(dbfile);
foreach (var line in lineOfContents)
{
string[] tokens = line.Split(',');
comboBox1.Items.Add(tokens[0]);
}
How do I expand this so it put e.g. DIRT and STONE in the combobox, and keep the rest (3) in variables (ints, like int varDIRT = 3)?
If you want, it doesn't have to be txt or db files.. i heard xml are config files too.
Try doing something like this:
cmb.DataSource = File.ReadAllLines("filePath").Select(d => new
{
Name = d.Split(',').First(),
Value = Convert.ToInt32(d.Split(',').Last().Replace(";",""))
}).ToList();
cmb.DisplayMember = "Name";
cmb.ValueMember= "Value";
remember it will require to use using System.Linq;
if your want ot reference the selected value of the combobox you can use
cmb.SelectedValue;
cmb.SelectedText;
I think you've really got two questions, so I'll try to answer them separately.
The first question is "How can I parse a file that looks like this...
DIRT: 3;
STONE: 6;
into names and integers?" You could remove all the whitespace and semicolons from each line, and then split on colon. A cleaner way, in my opinion, would be to use a regular expression:
// load your file
var fileLines = new[]
{
"DIRT: 3;",
"STONE: 6;"
};
// This regular expression will match anything that
// begins with some letters, then has a colon followed
// by optional whitespace ending in a number and a semicolon.
var regex = new Regex(#"(\w+):\s*([0-9])+;", RegexOptions.Compiled);
foreach (var line in fileLines)
{
// Puts the tokens into an array.
// The zeroth token will be the entire matching string.
// Further tokens will be the contents of the parentheses in the expression.
var tokens = regex.Match(line).Groups;
// This is the name from the line, i.e "DIRT" or "STONE"
var name = tokens[1].Value;
// This is the numerical value from the same line.
var value = int.Parse(tokens[2].Value);
}
If you're not familiar with regular expressions, I encourage you to check them out; they make it very easy to format strings and pull out values. http://regexone.com/
The second question, "how do I store the value alongside the name?", I'm not sure I fully understand. If what you want to do is back each item with the numerical value specified in the file, the dub stylee's advice is good for you. You'll need to place the name as the display member and value as the value member. However, since your data is not in a table, you'll have to put the data somewhere accessible so that the Properties you want to use can be named. I recommend a dictionary:
// This is your ComboBox.
var comboBox = new ComboBox();
// load your file
var fileLines = new[]
{
"DIRT: 3;",
"STONE: 6;"
};
// This regular expression will match anything that
// begins with some letters, then has a colon followed
// by optional whitespace ending in a number and a semicolon.
var regex = new Regex(#"(\w+):\s*([0-9])+;", RegexOptions.Compiled);
// This does the same as the foreach loop did, but it puts the results into a dictionary.
var dictionary = fileLines.Select(line => regex.Match(line).Groups)
.ToDictionary(tokens => tokens[1].Value, tokens => int.Parse(tokens[2].Value));
// When you enumerate a dictionary, you get the entries as KeyValuePair objects.
foreach (var kvp in dictionary) comboBox.Items.Add(kvp);
// DisplayMember and ValueMember need to be set to
// the names of usable properties on the item type.
// KeyValue pair has "Key" and "Value" properties.
comboBox.DisplayMember = "Key";
comboBox.ValueMember = "Value";
In this version, I have used Linq to construct the dictionary. If you don't like the Linq syntax, you can use a loop instead:
var dictionary = new Dictionary<string, int>();
foreach (var line in fileLines)
{
var tokens = regex.Match(line).Groups;
dictionary.Add(tokens[1].Value, int.Parse(tokens[2].Value));
}
You could also use FileHelpers library. First define your data record.
[DelimitedRecord(":")]
public class Record
{
public string Name;
[FieldTrim(TrimMode.Right,';')]
public int Value;
}
Then you read in your data like so:
FileHelperEngine engine = new FileHelperEngine(typeof(Record));
//Read from file
Record[] res = engine.ReadFile("FileIn.txt") as Record[];
// write to file
engine.WriteFile("FileOut.txt", res);