List of strings stored as values in dictionary C# - c#

I have a testing framework that needs to be updated to include testing in Spanish. I have a CSV file that contains the field label, english text, and Spanish text. I've decided to use a dictionary to store the field label as the key and the values would be a list of strings for Spanish and English text.
private List<string> ReadTranslationCsv()
{
var pathToCSV = #"C:\Location";
Dictionary<string, List<string>> translations = new Dictionary<string, List<string>>();
string label, englishText, spanishText;
using (TextReader fileReader = File.OpenText(pathToCSV))
{
var csv = new CsvReader(fileReader);
csv.Configuration.HasHeaderRecord = false;
while (csv.Read())
{
for (int i = 0; csv.TryGetField<string>(i, out label);)
{
List<string> Spanglish = new List<string>();
csv.TryGetField<string>(i + 1, out englishText);
Spanglish.Add(englishText);
csv.TryGetField<string>(i + 2, out spanishText);
Spanglish.Add(spanishText);
if (label != "")
{
translations.Add(label, Spanglish);
}
i = i + 3;
}
}
}
}
I want to be able to search within the list of values to see if anything matches some string of text. I'm not sure how to search the lists that are within the dictionary, none of the default methods or properties are working.
I'm using the below code but this will return me a bool, which is not what I need, I need the list value that matches the elementWithText
public void GivenElementMatches(string elementWithText)
{
if (Config.Language == "Spanish")
{
var list = new List<string> { elementWithText };//must create list in order to pass text to any translations methods
Hooks.translations.ContainsValue(list); // Even though the labels are the key, I need to search for the english text which is index 1 of the list and all values should be returned
}
//TODO
}

My suggestion would be to use a Dictionary with a class you create, inside that class you can have a compare function.
The advantage of this method is you may add more language equivalents later and only have to change your model.
Please note, this code is not complete and you will have to bug check and alter it to suit.
Dictionary <string, LangEquivalents> model;
public KeyValuePair<string, LangEquivalents> findField(string input)
{
return model.First(x=>x.Value.Comparison(input));
}
You could also make it a comparable object type and just use model.First(x=>x.Value == input));

Related

Select Row of CSV File [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 months ago.
Improve this question
I have a CSV File with these headers:
date;clock;value
My aim is to select the CSV line with a specific date to get the corresponding value.
For example:
I want to select date 20.08.22 and the result should be 130
15.08.22;07:05;100
20.08.22;08:04;130
21.08.22;10:04;150
With this code snippet I read the lines of the csv file:
private void Werte_aus_CSV_auslesen()
{
var path = #"E:\werte.csv";
using (TextFieldParser csvParser = new TextFieldParser(path))
{
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { ";" });
csvParser.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names
csvParser.ReadLine();
while (!csvParser.EndOfData)
{
// Read current line fields, pointer moves to the next line.
fields = csvParser.ReadFields();
datum.Add(fields[0]);
uhrzeit.Add(fields[1]);
wert.Add(double.Parse(fields[2], CultureInfo.InvariantCulture));
}
}
}
The approach you are using is going to have to scan the entire CSV every time you lookup a value. This might be a performance problem if this method is called multiple times. It would be better to build a dictionary that maps the date to the value that can be built once and reused for each subsequent lookup.
I maintain a couple libraries that make this pretty easy: Sylvan.Data and Sylvan.Data.Csv. Here is a complete C# 10 console app that demonstrates how to accomplish this:
using Sylvan.Data.Csv;
using Sylvan.Data;
// data: would normally use CsvDataReader.Create(csvFileName, opts);
var data =
new StringReader(
#"date;clock;value
15.08.22;07:05;100
20.08.22;08:04;130
21.08.22;10:04;150
");
// parameter:
var selectDate = new DateTime(2022, 8, 20);
// configure settings so the csv reader understands your data
var opts = new CsvDataReaderOptions
{
DateTimeFormat = "dd'.'MM'.'yy",
// ignore clock, as it isn't used
Schema = new CsvSchema(Schema.Parse("date:date,clock,value:int"))
};
var csvReader = CsvDataReader.Create(data, opts);
// create a dictionary to cache the CSV data for quick lookups
// creating the dictionary scans the whole dataset, but subsequent lookups will
// be blazing fast.
{
var dict =
csvReader
.GetRecords<Record>() // bind the CSV data to the Record class
.ToDictionary(r => r.Date, r => r.Value);
Console.WriteLine(dict.TryGetValue(selectDate, out var value) ? value.ToString() : "Value not found");
}
class Record
{
public DateTime Date { get; set; }
public int Value { get; set; }
}
Matched arrays/lists like datum, uhrzeit, and wert that relate values within each collection based on index is an anti-pattern... something to avoid. So much better to create a class with fields for each of the values, and then have one collection to hold the class.
public class MyData
{
public DateTime date {get;set;}
public int value {get;set;}
}
(Of course, give it a better name than "MyData")
Newer code might also use a record instead of a class.
We can further improve this by separating the code to read the csv data from the code that composes the objects. Start with something like this:
private IEnumerable<string[]> Werte_aus_CSV_auslesen(string path)
{
using (TextFieldParser csvParser = new TextFieldParser(path))
{
csvParser.CommentTokens = new string[] { "#" };
csvParser.SetDelimiters(new string[] { ";" });
csvParser.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names
csvParser.ReadLine();
while (!csvParser.EndOfData)
{
// Read current line fields, pointer moves to the next line.
yield return csvParser.ReadFields();
}
}
}
Notice how it accepts an input and returns an object (the enumerable with the data). Also notice how it avoids anything to do with processing the individual rows. It is only concerned with parsing the CSV/SSV inputs. It doesn't care what fields you expect to find, and can handle any file input with a header line, hash comments, and semi-colon field separators.
Since this gives us string[] values, we also add a method to transform a string[] into a class instance. I like to start out with this as a static method of the class itself, but as a project grows to have many of these methods they may eventually be moved to their own static type:
public class MyData
{
public DateTime date {get;set;}
public int value {get;set}
public static MyData FromCSVRow(string[] input)
{
return new MyData() {
date = DateTime.ParseExact($"{input[0]} {input[1]}", "dd.MM.yy HH:mm", null),
value = int.Parse(input[2])
};
}
}
And now with all that out of the way, we can finally put it all together to get your answer:
var targetDate = new DateTime(2022, 8, 20);
var csv = Werte_aus_CSV_auslesen(#"E:\werte.csv");
var rows = csv.Select(MyData.FromCSV);
var result = rows.Where(r => r.date.Date == targetDate);
If we really wanted to, we could even treat all that as a single line of code (it's probably better to keep it separate, for readability/maintainability):
var result = Werte_aus_CSV_auslesen(#"E:\werte.csv").
Select(MyData.FromCSV).
Where(r => r.date.Date == new DateTime(2022, 8, 20));
Note result is still an IEnumerable<MyData>, because there might be more than one row matching the criteria. If you are really sure there will only be one matching record, you can use this:
var result = rows.Where(r => r.date.Date == targetDate).FirstOrDefault();
or this:
var result = rows.Where(r => r.date.Date == targetDate).First();
depending on what you want to happen if no match is found.
One of the nice features here is this checks each record as it reads the file, and will stop reading the file as soon as it finds a match, which is potentially a very nice performance win.

C# Converting List to 2d list and adding additional values

Hello need some assistance with this issue. Hopefully i can describe it well.
I have a parser that goes though a document and find sessionID's, strips some tags from them and places them into a list.
while ((line = sr.ReadLine()) != null)
{
Match sID = sessionId.Match(line);
if (sID.Success)
{
String sIDString;
String sid = sID.ToString();
sIDString = Regex.Replace(sid, "<[^>]+>", string.Empty);
sessionIDList.Add(sIDString);
}
}
Then I go thought list and get the distinctSessionID's.
List<String> distinctSessionID = sessionIDList.Distinct().ToList();
Now I need to go thought he document again and add the lines that match the sessionID and add them to the list. This is the part that I am having issue with.
Do I need to create a 2d list so I can add the matching log lines to the corresponding sessionids.
I was looking at this but cannot seem to figure out a way that I could copy over my Distinct list then add the Lines I need into the new array.
From what I can test it looks like this would add the value into the masterlist
List<List<string>> masterLists = new List<List<string>>();
Foreach (string value in distinctSessionID)
{
masterLists[0].Add(value);
}
How do I add Lines I need to the corresponding Masterlist. Say masterList[0].Add value is 1, how do i add the lines to 1?
masterList[0][0].add(myLInes);
Basically i want
Sessionid1
-------> related log line
-------> Related log line
SessionID2
-------> related log line
-------> related log line.
So on and so forth. I have the parsing all working, it's just getting the values into a 2nd string list is the issue.
Thanks,
What you can do is, simple create a class with public properties, and make list of that custom class.
public class Session
{
public int SessionId{get;set;}
public List<string> SessionLog{get;set;}
}
List<Session> objList = new List<Session>();
var session1 = new Session();
session1.SessionId = 1;
session1.SessionLog.Add("description lline1");
objList.Add(session1);
Here is one way to do it:
public class MultiDimDictList: Dictionary<string, List<int>> { }
MultiDimDictList myDictList = new MultiDimDictList ();
Foreach (string value in distinctSessionID)
{
myDictList.Add(value, new List<int>());
for(int j=0; j < lengthofLines; j++)
{
myDictList[value].Add(myLine);
}
}
You would need to replace lengthofLines with a number to indicate how many iterations of lines you have.
See Charles Bretana's answer here

Creating and Displaying multiple arrays simultaniously C#

I'm new to C# and programming as a whole and I've been unable to come up with a solution to what I want to do. I want to be able to create a way to display several arrays containing elements from three external text files with values on each line (e.g. #"Files\Column1.txt", #"Files\Column2.txt" #"Files\Column3.txt"). They then need to be displayed like this in the command line:
https://www.dropbox.com/s/0telh1ils201wpy/Untitled.png?dl=0
I also need to be able to sort each column individually (e.g. column 3 from lowest to highest).
I've probably explained this horribly but I'm not sure how else to put it! Any possible solutions will be greatly appreciated!
One way to do it would be to store the corresponding items from each file in a Tuple, and then store those in a List. This way the items will all stay together, but you can sort your list on any of the Tuple fields. If you were doing anything more detailed with these items, I would suggest creating a simple class to store them, so the code would be more maintainable.
Something like:
public class Item
{
public DayOfWeek Day { get; set; }
public DateTime Date { get; set; }
public string Value { get; set; }
}
The example below could easily be converted to use such a class, but for now it uses a Tuple<string, string, string>. As an intermediate step, you could easily convert the items as you create the Tuple to get more strongly-typed versions, for example, you could have Tuple<DayOfWeek, DateTime, string>.
Here's the sample code for reading your file items into a list, and how to sort on each item type:
public static void Main()
{
// For testing sake, I created some dummy files
var file1 = #"D:\Public\Temp\File1.txt";
var file2 = #"D:\Public\Temp\File2.txt";
var file3 = #"D:\Public\Temp\File3.txt";
// Validation that files exist and have same number
// of items is intentionally left out for the example
// Read the contents of each file into a separate variable
var days = File.ReadAllLines(file1);
var dates = File.ReadAllLines(file2);
var values = File.ReadAllLines(file3);
var itemCount = days.Length;
// The list of items read from each file
var fileItems = new List<Tuple<string, string, string>>();
// Add a new item for each line in each file
for (int i = 0; i < itemCount; i++)
{
fileItems.Add(new Tuple<string, string, string>(
days[i], dates[i], values[i]));
}
// Display the items in console window
fileItems.ForEach(item =>
Console.WriteLine("{0} {1} = {2}",
item.Item1, item.Item2, item.Item3));
// Example for how to order the items:
// By days
fileItems = fileItems.OrderBy(item => item.Item1).ToList();
// By dates
fileItems = fileItems.OrderBy(item => item.Item2).ToList();
// By values
fileItems = fileItems.OrderBy(item => item.Item3).ToList();
// Order by descending
fileItems = fileItems.OrderByDescending(item => item.Item1).ToList();
// Show the values based on the last ordering
fileItems.ForEach(item =>
Console.WriteLine("{0} {1} = {2}",
item.Item1, item.Item2, item.Item3));
}

Dictionary<int, List<string>>

I have something like this:
Dictionary<int, List<string>> fileList = new Dictionary<int, List<string>>();
and then, I fill it with some variables, for example:
fileList.Add(
counter,
new List<string> {
OFD.SafeFileName,
OFD.FileName,
VERSION, NAME , DATE ,
BOX , SERIAL_NUM, SERIES,
POINT , NOTE , VARIANT
}
);
Where counter is a variable that increment +1 each time something happens, List<string>{XXX} where XXX are string variables that holds some text.
My question is, how do I access these strings from the list, if counter == 1?
You can access the data in the dictionary and lists just like normal. Remember, access a value in the dictionary first, which will return a list. Then, access the items in the list.
For example, you can index into the dictionary, which returns a list, and then index into the list:
------ Returns a list from the dictionary
| --- Returns an item from the list
| |
v v
fileList[0][0] // First item in the first list
fileList[1][0] // First item in the second list
fileList[1][1] // Second item in the second list
// etc.
FishBasketGordo explains how you can access entries in your data structure. I will only add some thoughts here:
Dictionaries (based on hash tables) allow fast access to arbitrary keys. But your keys are given by a counter variable (counter = 0, 1, 2, 3, 4 ...). The fastest way to access such keys is to simply use the index of an array or of a list. Therefore I would just use a List<> instead of a Dictionary<,>.
Furthermore, your list seems not to list anonymous values but rather values having very specific and distinct meanings. I.e. a date is not the same as a name. In this case I would create a class that stores these values and that allows an individual access to individual values.
public class FileInformation
{
public string SafeFileName { get; set; }
public string FileName { get; set; }
public decimal Version { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
...
}
Now you can create a list like this:
var fileList = new List<FileInformation>();
fileList.Add(
new FileInformation {
SafeFileName = "MyDocument.txt",
FileName = "MyDocument.txt",
Version = 1.2,
...
}
}
And you can access the information like this
decimal version = fileList[5].Version;
If the keys don't start at zero, just subtract the starting value:
int firstKey = 100;
int requestedKey = 117;
decimal version = fileList[requestedKey - firstKey].Version;
Dictionary uses Indexer to access its vallues via key.
List<string> items = fileList[counter];
var str0 = items[0];
var str1 = items[1];
Then you can do anything with the list.
Dictionary<int, List<string>> fileList = new Dictionary<int, List<string>>();
fileList.Add(101, new List<string> { "fijo", "Frigy" });
fileList.Add(102, new List<string> { "lijo", "liji" });
fileList.Add(103, new List<string> { "vimal", "vilma" });
for (int Key = 101; Key < 104; Key++)
{
for (int ListIndex = 0; ListIndex < fileList[Key].Count; ListIndex++)
{
Console.WriteLine(fileList[Key][ListIndex] as string);
}
}
You can access the List through MyDic[Key][0]. While editing the list, there won't be any run time errors, however it will result in unnecessary values stored in Dictionary. So better:
assign the MyDict[Key] to new list
edit the new list and then
reassign the new list to MyDict[Key] rather than editing a
particular variable in the Dictionary with List as Values.
Code example:
List<string> lstr = new List<string(MyDict[Key]);
lstr[0] = "new Values";
lstr[1] = "new Value 2";
MyDict[Key] = lstr;

how to dissect string values

How can I dissect or retrieve string values?
Here's the sample code that I'm working on now:
private void SplitStrings()
{
List<string> listvalues = new List<string>();
listvalues = (List<string>)Session["mylist"];
string[] strvalues = listvalues.ToArray();
for (int x = 0; x < strvalues.Length; x++)
{
}
}
Now that I'am able to retrieve list values in my session. How can I separately get the values of each list using foreach or for statement?
What I want to happen is to programmatically split the values of the strings depending on how many is in the list.
If you have a list of string values, you can do the following:
private void SplitStrings()
{
List<string> listValues = (List<string>) Session["mylist"];
// always check session values for null
if(listValues != null)
{
// go through each list item
foreach(string stringElement in listValues)
{
// do something with variable 'stringElement'
System.Console.WriteLine(stringElement);
}
}
}
Note that I test the result of casting the session and that I don't create a new list first-off, which is not necessary. Also note that I don't convert to an array, simply because looping a list is actually easier, or just as easy, as looping an array.
Note that you named your method SplitStrings, but we're not splitting anything. Did you mean to split something like "one;two;three;four" in a four-element list, based on the separator character?
I'm not sure what you're trying to obtain in this code, I don't know why you're converting your List to an Array.
You can loop through your listValues collection with a foreach block:
foreach(string value in listValues)
{
//do something with value, I.e.
Response.Write(value);
}
I don't know what's in the strings but you can start by simplifying. There is no point allocating a new List if you're going to overwrite it immediately.
private void SplitStrings()
{
List<string> list = (List<string>)Session["mylist"];
foreach(string value in list)
{
}
}
List listvalues = (List)Session["mylist"];
foreach (string s in listvalues)
{
//do what you want with s here
}

Categories

Resources