Dictionaries in C# - c#

This program is meant to read in a csv file and create a dictionary from it, which is then used to translate a word typed into a textbox (txtINPUT) and output the result to another textbox (txtOutput).
The program doesnt translate anything and always outputs "No translation found."
I've never used the dictionary class before so I dont know where the problem is coming from.
Thanks for any help you can give me.
Dictionary<string, string> dictionary;
private void CreateDictionary()
{
//Load file
List<string> list = new List<string>();
using (StreamReader reader = new StreamReader("dictionarylist.csv"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
//Add to dictionary
dictionary = new Dictionary<string, string>();
string[] split = line.Split(',');
dictionary.Add(split[0], split[1]);
}
}
}
private void btnTranslate_Click(object sender, EventArgs e)
{
CreateDictionary();
string outputString = null;
if (dictionary.TryGetValue(txtInput.Text, out outputString))
{
txtOutput.Text = outputString;
}
else
{
txtOutput.Text = ("No translation found");
}
}

You are creating a new instance of a Dictionary each loop cycle, basically overwriting it each time you read a line. Move this line out of the loop:
// Instantiate a dictionary
var map = new Dictionary<string, string>();
Also why not load dictionary one time, you are loading it each button click, this is not efficient.
(>=.NET 3) The same using LINQ ToDictionary():
usign System.Linq;
var map = File.ReadAllLines()
.Select(l =>
{
var pair = l.Split(',');
return new { First = pair[0], Second = pair[1] }
})
.ToDictionary(k => k.First, v => v.Second);

In your while loop, you create a new dictionary every single pass!
You want to create one dictionary, and add all the entries to that:
while ((line = reader.ReadLine()) != null)
{
//Add to dictionary
dictionary = new Dictionary<string, string>(); /* DON'T CREATE NEW DICTIONARIES */
string[] split = line.Split(',');
dictionary.Add(split[0], split[1]);
}
You should do it more like this:
List<string> list = new List<string>();
dictionary = new Dictionary<string, string>(); /* CREATE ONE DICTIONARY */
using (StreamReader reader = new StreamReader("dictionarylist.csv"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
string[] split = line.Split(',');
dictionary.Add(split[0], split[1]);
}
}

Related

Serialize a csv to json with header record using json.net newtonsoft

I have a simple csv that I am trying to serialize to json. How do I include the header record as the name in the name value pairs?
JsonSerializer serializer = new JsonSerializer();
var csv = new List<string[]>();
var lines = System.IO.File.ReadAllLines(file);
foreach (string line in lines)
csv.Add(line.Split(','));
string json = JsonConvert.SerializeObject(csv, Formatting.Indented);
You can use a List of Dictionary<string,string> but you also need to get the header row from the csv. I have a rough idea for you below.
// initialize the list
var list = new List<Dictionary<string, string>>();
var lines = System.IO.File.ReadAllLines("");
// get your header values
var headers = lines[0].Split(',');
// Here you want to skip the first line because it is the header
foreach (string line in lines.Skip(1))
{
// split your line to get individual values
var lineSplit = line.Split(',');
// make a dictionary to hold the line values
var dictionary = new Dictionary<string, string>();
// do a for loop to apply your headers
for (int i = 0; i < headers.Length; i++)
{
dictionary.Add(headers[i], lineSplit[i]);
}
// Add your dictionary to the list
list.Add(dictionary);
}
string json = JsonConvert.SerializeObject(list, Formatting.Indented);
The above will give you an array that is not wrapped in an object. If you would like something that will wrap it into an object you can just make a simple class to take care of that. Example below.
public class CsvToJson
{
public CsvToJson()
{
this.List = new List<Dictionary<string, string>>();
}
public CsvToJson(string filePath)
{
this.List = new List<Dictionary<string, string>>();
// Adding some exception prevention
if (File.Exists(filePath))
{
ConvertFromCsvToJson(filePath);
}
}
public List<Dictionary<string, string>> List { get; set; }
private void ConvertFromCsvToJson(string filePath)
{
var lines = File.ReadAllLines(filePath);
// get your header values
var headers = lines[0].Split(',');
foreach (string line in lines.Skip(1))
{
// split your line to get individual values
var lineSplit = line.Split(',');
// make a dictionary to hold the line values
var dictionary = new Dictionary<string, string>();
// do a for loop to apply your headers
for (int i = 0; i < headers.Length; i++)
{
dictionary.Add(headers[i], lineSplit[i]);
}
// Add your dictionary to the list
List.Add(dictionary);
}
}
}
Then you could easily call on this anytime by using something like below.
var rootObject = new CsvToJson("C:\testFile.csv");
var json = JsonConvert.SerializeObject(rootObject, Formatting.Indented);

How do I add multiple values to one key in a dictionary and display the duplicates? [duplicate]

This question already has answers here:
Console.WriteLine and generic List
(9 answers)
Closed 4 years ago.
I am working with an input file that has a list of guild names and then guild servers for those guild names. My problem is that for some guilds there is a duplicate, meaning that the same guild exists on different servers. So I need the guildName, read from my input file, to display the different server it is on. Here is the code I have so far:
private void buildGuilds()
{
using (StreamReader inFile = new StreamReader("guilds.txt"))
{
string str = null;
char[] delimiters = {'-', '\t'};
//while the buffer isn't empty
while ((str = inFile.ReadLine()) != null)
{
SortedList<string, string> guild = new SortedList<string, string>();
string[] Buffer = str.Split(delimiters);
guildName = Buffer[1];
guildServer = Buffer[2];
if (!dictGuilds.ContainsKey(guildName))
{
dictGuilds.Add(guildName, new List<string>());
}
dictGuilds[guildName].Add(guildServer);
So my program reads the data into 2 variables and then determines what values go where but I cannot get it to print when using the conventional foreach pair.Key pair.Value method. Here is my print method as well.
private void printGuilds()
{
foreach (KeyValuePair<string, List<string>> pair in dictGuilds)
{
Guilds_List.Items.Add(pair.Key + pair.Value);
}
}
any help i could get would be great. Thank you so much
I think the below may be what you're looking for, though I'm not certain exactly what you're seeking to do (feel free to let me know if I'm missing something).
I achieved the following by:
Creating an empty class library project
Adding the xunit and fluentassertions nuget packages.
This allows you to run the unit tests that verify this assertion.
If I misunderstood the question, let me know and I'll try to adapt it for you. Note that I changed the input from a file input for the purposes of writing the tests & verifying things.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace MultiValueDictionaryTests
{
public class MultiValueDictionaryTests
{
[Fact]
public void UnderstandsPairsCorrectly()
{
/* Mimics the following file contents:
Guild1-ServerDEF
Guild2-ServerDEF
Guild2-ServerABC
Guild1 ServerXYZ
Guild2-ServerABC
Guild2-ServerABC
*/
var testString = "Guild1-ServerDEF\r\nGuild2-ServerDEF\r\nGuild2-ServerABC\r\nGuild1\tServerXYZ\r\nGuild2-ServerABC\r\nGuild2-ServerABC\r\n";
var builder = new GuildBuilder();
var result = builder.BuildGuilds(testString);
result.Should().ContainKey("Guild1");
result.Should().ContainKey("Guild2");
result["Guild1"].Should().ContainKey("ServerDEF")
.And.ContainKey("ServerXYZ");
result["Guild1"].Should().NotContainKey("ServerABC");
result["Guild2"].Should().ContainKey("ServerDEF")
.And.ContainKey("ServerABC");
result["Guild2"].First().Key.Should().Be("ServerABC");
}
[Fact]
public void WriteLine_ShowsCommaSeparatedValues()
{
var builder = new GuildBuilder();
var example = new SortedDictionary<string, SortedList<string, string>>
{
{
"Guild1", new SortedList<string, string> {{"ServerABC", "ServerABC"}, {"ServerDEF", "ServerDEF"}}
},
{
"Guild2", new SortedList<string, string> {{"ServerABC", "ServerABC"}, {"ServerXYZ", "ServerXYZ"}}
}
};
List<string> resultLines = builder.WriteGuildLines(example);
resultLines.First().Should().Be("Guild1 - ServerABC, ServerDEF");
resultLines.Last().Should().Be("Guild2 - ServerABC, ServerXYZ");
}
}
public class GuildBuilder
{
readonly char[] delimiters = { '-', '\t' };
public SortedDictionary<string, SortedList<string,string>> BuildGuilds(string inputString) // instead of file
{
var result = new SortedDictionary<string, SortedList<string,string>>();
using (var reader = new StringReader(inputString))
{
string line;
while ((line = reader.ReadLine()) != null)
{
var splitArray = line.Split(delimiters);
var guild = splitArray[0];
var server = splitArray[1];
if (!result.ContainsKey(guild))
{
result.Add(guild, new SortedList<string,string>());
}
if (!result[guild].ContainsKey(server))
{
result[guild].Add(server, server);
}
}
}
return result;
}
public List<string> WriteGuildLines(SortedDictionary<string, SortedList<string, string>> input)
{
var result = new List<string>();
foreach (var item in input)
{
result.Add($"{item.Key} - {string.Join(", ", item.Value.Keys)}");
}
return result;
}
}
}

Extracting a dictionary from sparse csv file

I have a sparsely populated excel file I want to extract two columns into a dictionary in C#. I have tried the following. This fails when it reads the blank lines. Is there a cleaner way to achieve the same. I don't care about any other values here. Just a mapping of AR ID to AR Type would do.
public class Table
{
private Dictionary<string, string> _ARID_ARTypeValues = new Dictionary<string, string>();
private string _arId;
public Table(string arId)
{
_arId = arId;
}
public void AddValue(string key, string value)
{
_ARID_ARTypeValues.Add(key, value);
}
}
public static IDictionary ParseCsvFile(StreamReader reader)
{
Dictionary<string, Table> tables = new Dictionary<string, Table>();
// First line contains column names.
var columnNames = reader.ReadLine().Split(',');
for (int i = 1; i < columnNames.Length; ++i)
{
var columnName = columnNames[i];
var ntable = new Table(columnName);
if ((columnName == "AR ID") || (columnName == "AR Type"))
{
tables.Add(columnName, ntable);
}
}
var line = reader.ReadLine();
while (line != null)
{
var columns = line.Split(',');
for (int j = 1; j < columns.Length; ++j)
{
var table = tables[columnNames[j]];
table.AddValue(columns[0], columns[j]);
}
line = reader.ReadLine();
}
return tables;
}
I would just use a CSV library, like CsvHelper and read the csv file with that.
Dictionary<string, string> arIdToArTypeMapping = new Dictionary<string, string>();
using (var sr = File.OpenText("test.csv"))
{
var csvConfiguration = new CsvConfiguration
{
SkipEmptyRecords = true
};
using (var csvReader = new CsvReader(sr, csvConfiguration))
{
while (csvReader.Read())
{
string arId = csvReader.GetField("AR ID");
string arType = csvReader.GetField("AR Type");
if (!string.IsNullOrEmpty(arId) && !string.IsNullOrEmpty(arType))
{
arIdToArTypeMapping.Add(arId, arType);
}
}
}
}
You can use Cinchoo ETL - an open source library, to read the csv and convert them to dictionary as simple as with few lines of code shown below
using (var parser = new ChoCSVReader("Dict1.csv")
.WithField("AR_ID", 7)
.WithField("AR_TYPE", 8)
.WithFirstLineHeader(true)
.Configure(c => c.IgnoreEmptyLine = true)
)
{
var dict = parser.ToDictionary(item => item.AR_ID, item => item.AR_TYPE);
foreach (var kvp in dict)
Console.WriteLine(kvp.Key + " " + kvp.Value);
}
Hope this helps.
Disclaimer: I'm the author of this library.

Using array to replace string

I have a two arrays i have taken from a csv file, i want to check my current output for the first array and output the second array, eg
"Hello, LOL" would output "Hello, Laugh out loud"
i have used
var reader = new StreamReader(File.OpenRead(#filelocation"));
List<string> listA = new List<string>();
List<string> listB = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
listA.Add(values[0]);
listB.Add(values[1]);
}
The arrays are stored and have the correct information in them, i just don't know how to check a string from the first list and change it to the second.
You should use a Dictionary for this operation instead of List. In addition, you could use File.ReadAllLines to read all lines in a file instead of looping.
// I replace your code with linq
Dictionary<string, string> dictionary =
File.ReadAllLines("#filelocation").Select(l => l.Split(',')).ToDictionary(k =>
k[0], v => v[1]);
string input = "Hello, LOL" ;
var thekey = dictionary.Keys.FirstOrDefault(k => input.Contains(k));
if (thekey != null) // Replacement was found
{
input = input.Replace(thekey, dictionary[thekey]);
}
// Should print Hello, Laugh out loud
Console.WriteLine(input) ;

Read text file from specific position and store in two arrays

I have text file which contains line like this:
#relation SMILEfeatures
#attribute pcm_LOGenergy_sma_range numeric
#attribute pcm_LOGenergy_sma_maxPos numeric
#attribute pcm_LOGenergy_sma_minPos numeric...
Where are about 6000 lines of these attributes, after attributes where are lines like this:
#data
1.283827e+01,3.800000e+01,2.000000e+00,5.331364e+00
1.850000e+02,4.054457e+01,4.500000e+01,3.200000e+01...
I need to seperate these strings in two different arrays. So far I only managed to store everything in one array.
Here is my code for storing in array:
using (var stream = new FileStream(filePath, FileMode.OpenOrCreate))
{
using (var sr = new StreamReader(stream))
{
String line;
while ((line = sr.ReadLine()) != null)
{
sb.AppendLine(line);
}
}
string allines = sb.ToString();
Console.WriteLine(sb);
}
All strings after #relation SMILEfeatures and contains #attribute are stored in first array. All the strings after #data should are stored in the second array. Hope this is what you wanted.
var relationLineNumbers = new List<int>();
var dataLineNumbers = new List<int>();
var relation = new StringBuilder();
var data = new List<string>();
using (var stream = new FileStream(filepath, FileMode.OpenOrCreate))
{
using (var sr = new StreamReader(stream))
{
string line;
bool isRelation = false;
bool isData = false;
int lineNumber = 0;
while ((line = sr.ReadLine()) != null)
{
lineNumber++;
if (line.StartsWith("#relation SMILEfeatures"))
{
isRelation = true;
isData = false;
continue;
}
if (line.StartsWith("#data"))
{
isData = true;
isRelation = false;
continue;
}
if (isRelation)
{
if (line.StartsWith("#attribute"))
{
relation.Append(line);
relationLineNumbers.Add(lineNumber);
}
}
if (isData)
{
data.AddRange(line.Split(','));
dataLineNumbers.Add(lineNumber);
}
}
}
Console.WriteLine("Relation");
Console.WriteLine(relation.ToString());
Console.WriteLine("Data");
data.ForEach(Console.WriteLine);
All strings which starts with #relation SMILEfeatures and contains #attribute should be stored in first array. Numbers which starts with #data should be stored in second array.
Use string.Contains() and string.StatsWith() for checking.
Read every line and decide in wich array / list you want to put this line
void ReadAndSortInArrays(string fileLocation)
{
List<string> noData = new List<string>();
List<string> Data = new List<string>();
using(StreamReader sr = new StreamReader(fileLocation))
{
string line;
while(!sr.EndOfStream)
{
line = sr.ReadLine();
if(line.StartsWith("#relation") && line.Contains("#attribute"))
{
noData.Add(line);
}
else if(line.StartsWith("#data")
{
Data.Add(line);
}
else
{
// This is stange
}
}
}
var noDataArray = noData.ToArray();
var DataArray = Data.ToArray();
}
But i think that not every line is beginning with "#data"
So you may want to Read all lines and do somethink like this:
string allLines;
using(StreamReader sr = new StreamReader(yourfile))
{
allLines = = sr.ReadToEnd();
}
var arrays = allLines.Split("#data");
// arrays[0] is the part before #data
// arrays[1] is the part after #data (the numbers)
// But array[1] does not contain #data
The question is not really very clear. But my take is, collect all lines that start with #relation or #attribute in one bucket, then collect all number lines in another bucket. I have chosen to ignore the #data lines, as they do not seem to contain any extra information.
Error checking may be performed by making sure that the data lines (i.e. number lines) contain comma separated lists of parsable numerical values.
var dataLines = new List<string>();
var relAttLines = new List<string>();
foreach (var line in File.ReadAllLines())
{
if (line.StartsWith("#relation") || line.StartsWith("#attribute"))
relAttLines.Add(line);
else if (line.StartsWith("#data"))
//ignore these
continue;
else
dataLines.Add(line);
}

Categories