I have a string that's like that:
UNIQUE_ID-String-TriggerID
82-DEFAULT-4;MINIMODE_ARCADE-1;MINIMODE_RUNNER-4;47-STARLING-1;
Some Input Examples:
1) 82-Mode1-4;
2) 4-Arcade-2;9-Runner-2;90-STARLING-1; // here you see Unique ID 4 has two entries
3) 82-DEFAULT-4;MINIMODE_ARCADE-1;MINIMODE_RUNNER-4;47-STARLING-1; 2-DEFAULT-4;MINIMODE_ARCADE-0;
// here 82 UNIQUE ID and 2 UNIQUE ID has two enteritis, but 47 UNIQUE ID has just one enterey
The problem is sometimes I get only one entry for the UniqueID 47, and sometimes I get for ID 82 multiple entries.
How would I split and parse the string correctly?
I'm putting the parsed string in a dictionary, so sometimes I get the same key to be added and that raises an exception
I have done the following but still can't trace all situations:
String data = Encoding.UTF8.GetString(Convert.FromBase64String(Progress));
String[] progressEntries = data.Split(';');
for (int i = 0; i < progressEntries.Length; i++)
{
String entry = progressEntries[i];
String[] values = entry.Split('-');
if (values.Length > 2)
{
Dictionary<string, int> dict = new Dictionary<string, int>();
dict[values[1]] = Convert.ToInt32(values[2]);
Progress.Add(Convert.ToInt32(values[0]), dict);
}
}
}
This is not too clear but let's give it a try.
In the text you provided:
82-DEFAULT-4;MINIMODE_ARCADE-1;MINIMODE_RUNNER-4;47-STARLING-1;
you have two groups. Each group has key-value pairs
group 1 - key: 82
DEFAULT: 4
MINIMODE_ARCADE: 1
MINIMODE_RUNNER: 4
group 2 - key: 47
STARLING: 1
If this is the structure you want to obtain then one storage possibility would be a Dictionary<int,Dictionary<string,int>>
I think the following code will do:
public class Parser
{
public Dictionary<int, Dictionary<string, int>> Parse(string input)
{
Dictionary<int, Dictionary<string, int>> data = new Dictionary<int, Dictionary<string, int>>();
int? currentGroupKey = null;
string[] keyValuePairs = input.Split(new char[] { ';' });
foreach (var kvp in keyValuePairs)
{
string[] tokens = kvp.Split(new char[] { '-' });
switch (tokens.Length)
{
case 2:
{
if (currentGroupKey.HasValue)
{
int groupKey = currentGroupKey.Value;
AddKeyValuePair(data, groupKey, tokens[0], tokens[1]);
}
break;
}
case 3:
{
int groupKey;
if (int.TryParse(tokens[0], out groupKey))
{
currentGroupKey = groupKey;
AddKeyValuePair(data, groupKey, tokens[1], tokens[2]);
}
break;
}
default:
break;
}
}
return data;
}
private void AddKeyValuePair(Dictionary<int, Dictionary<string, int>> data, int groupKey, string key, string val)
{
Dictionary<string, int> group;
if (data.ContainsKey(groupKey))
{
group = data[groupKey];
}
else
{
group = new Dictionary<string, int>();
data[groupKey] = group;
}
int intVal;
if (int.TryParse(val, out intVal))
group.Add(key, intVal);
}
}
Related
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 1 year ago.
Improve this question
I have a csv file with repetitive keys and multiple values from a csv. I have successfully parsed every row of csv in C#
My csv file looks like this
eid,hr
ABC,25
ABC,35
FDG,50
LMN,61
Task1
I would want to construct a dictionary
like
Dictionary<string,int[]> or Dictonary<string,List>
key1: ABC value1: 25
key2: ABC value2: 35
key3: FDG value3: 50
key4: LMN value4: 61
Task 2
Iterate through keys and make sure the total of the values for the same key is more than 50.
value1 and value2 will be proportioned to make the total as 50.
Here is the solution:
namespace CSVRead {class Program
{
static void Main(string[] args)
{
string[] csvLines = System.IO.File.ReadAllLines("//TheCSVfilepath");
var eID = new List<string>();
var hr = new List<double>();
for ( int i = 1; i < csvLines.Length; i++)
{
string[] rowData = csvLines[i].Split(',');
eID.Add(rowData[0]);
double hre = Convert.ToDouble(rowData[7]);
hr.Add(hre);
}
Console.WriteLine("eidDict: ");
for ( int m =0 ; m < eID.Count ; m++)
{
List<(string Key, double Value)> list = new List<(string Key, double Value)>; //CS1526Error
var eIDdictlist = list;
for (int n =0; n < hr.Count; n++)
{
employedictlist.Add(new KeyValuePair<string, double>(eID[m], hr[n])); //CS1503 Error
Console.WriteLine(eIDdictlist);
}
}
Console.ReadKey();
}
}
Your code doesn't seem to do remotely what your question is asking about. Here's what you need to do to get a dictionary of the list of values:
static void Main(string[] args)
{
var csvLines = File.ReadAllLines("//TheCSVfilepath");
var dictionary = new Dictionary<string, List<double>>();
foreach (var line in csvLines)
{
var (eID, hr) = GetEidAndHr(line);
if (!dictionary.TryGetValue(eID, out var hrList))
{
hrList = new List<double>();
dictionary[eID] = hrList;
}
hrList.Add(hr);
}
Console.WriteLine("eidDict: ");
//foreach (var keyValuePair in dictionary) // for pre c# 8
foreach (var (eid,hrList) in dictionary)
{
// var eid = keyValuePair.Key;// for pre c# 8
// var hrList = keyValuePair.Value;// for pre c# 8
var sum = hrList.Sum();
Console.WriteLine($"{eid}, {sum}");
}
}
static Tuple<string, double> GetEidAndHr(string csvLine)
{
var rowData = csvLine.Split(',');
return new Tuple<string, double>(rowData[0],double.Parse(rowData[7]));
}
If you are just trying to get your code to compile, here is what you need to change:
List<(string Key, double Value)> list = new List<(string Key, double Value)>; //CS1526Error
Needs to become
List<(string Key, double Value)> list = new List<(string Key, double Value)>();
And you also need to declare your variable employedictlist before you use it.
I think the following code should work for you:
// Test data to mock data from the CSV file
static IEnumerable<(string key, double value)> ReadCsv() =>
new[]
{
("ABC", 42.0),
("ABC", 123.0),
("DEF", 35.0),
("DEF", 15.0)
};
static void Main(string[] args)
{
// Task 1: Constructing a Dictionary<string, List<double>>
var data = ReadCsv()
.GroupBy(entry => entry.key)
.ToDictionary(
group => group.Key,
group => group.Select(entry => entry.value));
// Task 2: Iterating through the keys and verifying the total
var isSuccess = data.All(entry => entry.Value.Sum() > 50);
if (isSuccess)
Console.WriteLine("The total of values for each and every entry is greater than 50");
else
Console.WriteLine("The total of values for each and every entry is not greater than 50");
}
To read data from a CSV file, I would suggest using something like:
static IEnumerable<(string key, double value)> ReadCsv(string filePath, uint nHeaderLines = 0)
{
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (var reader = new StreamReader(stream))
{
var lineIndex = 0u;
while (reader.ReadLine() is string dataEntry)
{
if (lineIndex++ < nHeaderLines // Skip header
|| string.IsNullOrWhiteSpace(dataEntry)) // Ignore blank lines
{
continue;
}
var dataElements = dataEntry.Split(',', StringSplitOptions.RemoveEmptyEntries);
var key = dataElements[0];
var value = double.Parse(dataElements[7], NumberStyles.Float, NumberFormatInfo.InvariantInfo);
yield return (key, value);
}
}
}
Please note, that for the real solution it would also make sense to check the data line if it has all the entries, i.e., that elements at index 0 and 7 always exist and are valid
I need to pull a bunch of key value pairs based on a predefined pattern from a string. An example of what I would need is this:
Pattern: {value1}-{value2}
String: Example-String
Result KVP:
{ Key: value1, value: Example },
{ Key: value2, value: String }
The catch is that the pattern could be pretty much anything (although the values I'd need to extract would always be surrounded in curly brackets), ie:
Pattern: {test1}\{test2}={value}
String: Example\Value=Result
Result KVP:
{ Key: test1, value: Example },
{ Key: test2, value: Value },
{ Key: value, value: Result }
What I have done so far isn't quite working and I'm quite certain that there has to be a more elegant way of doing this as opposed to my solution anyway so I thought I'd see if anyone here would have a good idea.
EDIT:
Here is essentially what I have so far (it's working, but IMO it's really ugly):
public List<KeyValuePair<string, string>> Example(string pattern, string input)
{
var values = new List<KeyValuePair<string, string>>();
var r1 = Regex.Matches(input, #"(\{[A-Z,a-z]*\})");
string newregex = string.Empty;
foreach (Match item in r1)
{
newregex = newregex.Replace(item.Value, "(.*?)"); //updates regex so that it adds this as a group for use later, ie: "{item1}-{item2}" will become "(.*?)-{item2}"
string field = item.Value.Substring(1, item.Value.Length - 2); // {test1} will return "test1"
values.Add(new KeyValuePair<string, string>(field, string.Empty));
}
newregex = $"{newregex}\\z"; // ensures that it matches to end of input
var r2 = Regex.Match(input, newregex);
// KVP index (used below)
int val = 0;
foreach (Group g in r2.Groups)
{
if (g.Value == input)
continue; // first group will be equal to input, ignore
values[val] = new KeyValuePair<string, string>(values[val].Key, g.Value); // update KVP at index with new KVP with the value
val++;
}
return values;
}
Unfortunately I don't know regular expressions very well, but one way to solve this is to walk through each character of the pattern string and create a list of keys and delimeters, after which we can walk through the search string, and find the index of each delimeter to get the current value, and then add a new KeyValuePair to a list.
Here's a rough sample that assumes good input:
public static List<KeyValuePair<string, string>> GetKVPs(string pattern, string search)
{
var results = new List<KeyValuePair<string, string>>();
var keys = new List<string>();
var delimeters = new List<string>();
var currentKey = string.Empty;
var currentDelimeter = string.Empty;
var processingKey = false;
// Populate our lists of Keys and Delimeters
foreach (var chr in pattern)
{
switch (chr)
{
case '}':
{
if (currentKey.Length > 0)
{
keys.Add(currentKey);
currentKey = string.Empty;
}
processingKey = false;
break;
}
case '{':
{
if (currentDelimeter.Length > 0)
{
delimeters.Add(currentDelimeter);
currentDelimeter = string.Empty;
}
processingKey = true;
break;
}
default:
{
if (processingKey)
{
currentKey += chr;
}
else
{
currentDelimeter += chr;
}
break;
}
}
}
if (currentDelimeter.Length > 0) delimeters.Add(currentDelimeter);
var lastDelim = -1;
// Find our Values based on the delimeter positions in the search string
for (int i = 0; i < delimeters.Count; i++)
{
var delimIndex = search.IndexOf(delimeters[i], lastDelim + 1);
if (delimIndex > -1)
{
var value = search.Substring(lastDelim + 1, delimIndex - lastDelim - 1);
results.Add(new KeyValuePair<string, string>(keys[i], value));
lastDelim = delimIndex + delimeters[i].Length - 1;
}
}
// Add the item after the final delimeter if it exists:
if (lastDelim > -1 && lastDelim < search.Length - 1)
{
results.Add(new KeyValuePair<string, string>(keys.Last(),
search.Substring(lastDelim + 1)));
}
return results;
}
And an example of it in action:
public static void Main(string[] args)
{
var results = GetKVPs(
"{greeting}, {recipient}, this is {sender}.",
"Hello, Dolly, this is Louis.");
foreach (var kvp in results)
{
Console.WriteLine($"{kvp.Key} = {kvp.Value}");
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
I need to display the List of a Dictionary based on the int key I've entered in the console and throw an error message if it out of the given bounds of inputs in the console as well.
public class Channel
{
const string displayChannel = "{0},{1},{2}";
private Dictionary <int, string> ChannelLookup =
new Dictionary <int, string> ()
{
{1, new List<string>{"[A]","[B]","[C]"}},
{2, new List<string>{"[A]"}
};
public string channelDisplay (int val)
{
if (!ChannelLookup.ContainsKey(val))
{
// throw exception
}
else
{
string display = string.Format(displayChannel, ChannelLookup[val]);
return display;
}
}
}
System.Format.Exception:'Index(zerobased) must be greater than or
equal to zero and less than the size of the argument list.
Just for completeness: Sometimes I use Aggregate instead of Join, since it gives you more control.
var values = ChannelLookup[val].Values;
var display = values.Aggregate((a, b) => $"{a}, {b}");
To use the Linq function Aggregate, you need to add System.Linq to your using directives.
You have to display arbitrary number of items (say, 3 - "[A]", "[B]", "[C]" or just 1 - "A"); let's Join them instead of using Format
public class Channel {
private Dictionary <int, List<string>> ChannelLookup =
new Dictionary <int, List<string>> () {
{1, new List<string>() {"[A]", "[B]", "[C]"} },
{2, new List<string>() {"[A]"} },
};
public string channelDisplay (int key) {
if (ChannelLookup.TryGetValue(key, out var items))
return string.Join(",", items);
else
throw new ArgumentException($"{nameof(key)} = {key} not found", nameof(key));
}
}
Or even
public string channelDisplay(int key) => ChannelLookup.TryGetValue(key, out var items)
? string.Join(",", items)
: throw new ArgumentException($"{nameof(key)} = {key} not found", nameof(key));
.NET doesn't let you have unused format parameters, i.e. you can't do this
string.Format("{0},{1}", "first value") without also providing a value for {1}.
Your best bet here is probably string.Join. string.Join will concatenate the values you provide to it, placing the specified delimiter between each value.
See the docs here
public class Channel
{
private Dictionary <int, string> ChannelLookup =
new Dictionary <int, string> ()
{
{1, new List<string>{"[A]","[B]","[C]"}},
{2, new List<string>{"[A]"}
};
public string channelDisplay (int val)
{
if(!ChannelLookup.ContainsKey(val))
{
// throw exception
}
else
{
string display = string.Join(",", ChannelLookup[val]);
return display;
}
}
}
So I have a dictionary and need to check each key entry to see if it contains a series of letters in a string(lets call this LETTERS). If the key has any letters that LETTERS does not, or it has more of a letter than LETTERS has, it has to be removed.(LETTERS is not known beforehand)
This is the code involved
Dictionary<string, int> wordHolder = new Dictionary<string, int>();
string LETTERS = Console.ReadLine();
for (int i = 0; i < LETTERS.Count(); i++)
{
for (int j = 0; j < wordHolder.Count; j++)
{
}
}
I think i have it this time if you still need an answer
The below has 5 keys... 3 of which can not be created from the contents of "LETTERS".
Dictionary<string, int> wordHolder = new Dictionary<string, int>();
wordHolder.Add("CEFBA",1);
wordHolder.Add("ZDFEEG",2);
wordHolder.Add("TYHRFG", 3);
wordHolder.Add("FFFFBBDD", 4);
wordHolder.Add("PCDATTY", 5);
var keysToRemove = new List<string>();
string myLetters = "ABCDEF";
var myLettersArray = myLetters.ToCharArray();
foreach (var keyToCheck in wordHolder)
{
var keyCannotBeCreatedFromLetters = false;
var keyArray = keyToCheck.Key.ToCharArray();
foreach (var letterExists in
from keyLetterToCheck in keyArray
where !keyCannotBeCreatedFromLetters
select myLettersArray.Any(a => a == keyLetterToCheck)
into letterExists
where !letterExists select letterExists)
{
keysToRemove.Add(keyToCheck.Key);
keyCannotBeCreatedFromLetters = true;
}
}
foreach (var key in keysToRemove)
{
wordHolder.Remove(key);
}
It correctly identifies the 2nd, 3rd and 5th key as not creatable.
Below is the same logic but as foreach loops. I find this often useful so you can see whats happening internally.
foreach (var keyToCheck in wordHolder)
{
var keyCannotBeCreatedFromLetters = false;
var keyArray = keyToCheck.Key.ToCharArray();
foreach (var keyLetterToCheck in keyArray)
{
if (keyCannotBeCreatedFromLetters)
continue;
var letterExists = myLettersArray.Any(a => a == keyLetterToCheck);
if (letterExists) continue;
keysToRemove.Add(keyToCheck.Key);
keyCannotBeCreatedFromLetters = true;
}
}
Assuming you want to return any KeyValuePair which Key contains all letter inside LETTERS.
It would look like this:
// Assuming
// Dictionary<string, int> wordHolder = new Dictionary<string, int>(); // Something
// string LETTERS = ""; // Something
List<char> listLettersToHave = LETTERS.ToList();
Dictionary<string, int> researchResult = new Dictionary<string, int>();
foreach (KeyValuePair<string, int> pair in wordHolder)
{
List<char> listLettersYouHave = pair.Key.ToList();
bool ok = true;
// If not the same count go to next KeyValuePair
if (listLettersToHave.Count != listLettersYouHave.Count)
continue;
foreach (char toCheck in listLettersToHave)
{
// Search first occurence
if (!listLettersYouHave.Contains(toCheck))
{
ok = false;
break;
}
// Remove first occurence
listLettersYouHave.Remove(toCheck);
}
if (ok)
// If all letters contained then Add to result
researchResult.Add(pair.Key, pair.Value);
}
// if it's a function
// return researchResult;
It's an example and you can improve it but the idea is here.
EDIT :
If the dictionnary have for keys : abc, cbda, dca
An input of bac
The results key would be : abc
The solution is case sensitive but a .ToUpper() will solve the problem.
Using the previous example, if you want cbda to match you can remove the check on Count.
I want to use a 2-D array in C#, e.g:
string[,] a = new string[,]
{
{"aunt", "AUNT_ID"},
{"Sam", "AUNT_NAME"},
{"clozapine", "OPTION"},
};
My requirement is that when I pass "aunt" to this array I want to get corresponding AUNT_ID from the 2-D array.
As others have said, a Dictionary<string, string> would be better - and you can use a collection initializer to create it simply:
Dictionary<string, string> dictionary = new Dictionary<string, string>
{
{"ant", "AUNT_ID"},
{"Sam", "AUNT_NAME"},
{"clozapine", "OPTION"},
};
If you're confident that your key is in the dictionary, and you're happy for an exception to be thrown otherwise:
string value = dictionary[key];
or if it might not be:
string value;
if (dictionary.TryGetValue(key, out value))
{
// Use value here
}
else
{
// Key wasn't in dictionary
}
If you really need to use an array, if you can change it to a multidimensional array (string[][]), you can use:
// Will throw if there are no matches
var value = array.First(x => x[0] == key)[1];
Or again to be more circumspect:
var pair = array.FirstOrDefault(x => x[0] == key);
if (pair != null)
{
string value = pair[1];
// Use value here
}
else
{
// Key wasn't in dictionary
}
LINQ unfortunately doesn't work quite as well on rectangular arrays. It probably wouldn't be too hard to write a method to allow it to be treated "somewhat" like an array of arrays, admittedly...
Use Dictionary<string, string> for that:
Dictionary<string, string> arr = new Dictionary<string, string>();
arr.Add("ant", "AUNT_ID");
arr.Add("Sam", "AUNT_NAME");
arr.Add("clozapine", "OPTION");
string k = arr["ant"]; // "AUNT_ID"
The best option for you is to use Dictionary, but if you still wants to use 2D array, you may try the following
string[,] a = new string[,]
{
{"ant", "AUNT_ID"},
{"Sam", "AUNT_NAME"},
{"clozapine", "OPTION"},
};
string search = "ant";
string result = String.Empty;
for (int i = 0; i < a.GetLength(0); i++) //loop until the row limit
{
if (a[i, 0] == search)
{
result = a[i, 1];
break; //break the loop on find
}
}
Console.WriteLine(result); // this will display AUNT_ID
It looks rather like you want a dictionary:
Dictionary<string, string> a = new Dictionary<string, string>();
a.Add("ant", "AUNT_ID");
a.Add("Sam", "AUNT_NAME");
a.Add("clozapine", "OPTION");
string s = a["ant"]; // gets "AUNT_ID"
To check if a key exists in the dictionary:
if (a.ContainsKey("ant")) {
...
}
Or:
string s;
if (a.TryGetValue("ant", out s)) {
...
}
for (i=0; i<3; i++){
if (!String.Compare(a[i][0], string)){
stored_string= a[i][1];
}
}