How to display/output/manipulate the contents of Dictionary? - c#

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;
}
}
}

Related

Iterate over dictionary values list in C# to check for specific key

I would like to iterate over dictionary values which is a list of strings in C# to check for all keys
Dictionary<string, List<string>> csvList = new Dictionary<string, List<string>>();
I want to check each key(string) in csvList and check if it exists in any values(List)
foreach(var events in csvList)
{
foreach(var action in csvList.Values) // But I want to loop through all the lists in dictionary, not just the list of event key
{
}
}
This is kind of strange but let's try to work through it. We don't usually want to iterate the keys of a dictionary. The reason to use one is we want to get the values very quickly if we already know the key.
In the spirit of answering the question, to iterate over a Dictionary's keys you have to use the Keys property. Note that nothing about the order of this collection is guaranteed.
var d = new Dictionary<string, int>();
d.Add("one", 1);
d.Add("two", 2);
foreach (var k in d.Keys) {
Console.WriteLine(k);
}
But I think maybe you had a problem and chose a Dictionary as the solution, then came here when that didn't work. What if the Dictionary isn't the problem?
It sounds like you have several List<string> instances and you're interested in if a particular list contains a particular string. Or maybe you want to know, "Which lists contain which string?" We can answer that with a dictionary structured slightly differently. I'm going to use arrays instead of lists because it's easier to type.
using System;
using System.Collections.Generic;
public class Program
{
private static void AddWord(Dictionary<string, List<int>> d, string word, int index) {
if (!d.ContainsKey(word)) {
d.Add(word, new List<int>());
}
d[word].Add(index);
}
private static List<int> GetIndexesForWord(Dictionary<string, List<int>> d, string word) {
if (!d.ContainsKey(word)) {
return new List<int>();
} else {
return d[word];
}
}
public static void Main()
{
var stringsToFind = new[] { "one", "five", "seven" };
var listsToTest = new[] {
new[] { "two", "three", "four", "five" },
new[] { "one", "two", "seven" },
new[] { "one", "five", "seven" }
};
// Build a lookup that knows which words appear in which lists, even
// if we don't care about those words.
var keyToIndexes = new Dictionary<string, List<int>>();
for (var listIndex = 0; listIndex < listsToTest.GetLength(0); listIndex++) {
var listToTest = listsToTest[listIndex];
foreach (var word in listToTest) {
AddWord(keyToIndexes, word, listIndex);
}
}
// Report which lists have the target words.
foreach (var target in stringsToFind) {
Console.WriteLine("Lists with '{0}':", target);
var indices = GetIndexesForWord(keyToIndexes, target);
if (indices.Count == 0) {
Console.WriteLine(" <none>");
} else {
var message = string.Join(", ", indices);
Console.WriteLine(" {0}", message);
}
}
}
}
foreach(var events in csvList)
{
foreach(var action in csvList.Values)
{
if (action.Contains(events.Key)) //Just use this, there is no point to iterate the list as you can use contains method
}
}

How to deserialize string to object (string in format in similar to object notation)

I have a string that looks like this. It's not JSON and not XML.
{Foo={name=My Foo Value, active=true, some date=20170630}, Bar={name=My Bar Value}, Key With Space={name=Foo Bar, active=false}}
Here are the assumptions:
Objects are enclosed by {}
Keys can have a space in the name
Keys are separated by a space and a comma (,)
Values are assigned to keys by an equal sign
Keys can have multiple values and values are enclosed by {}, values inside the value are separated by a space and a comma (,). For example, this is a single key with three values: {My Foo Key={one=true, two=true, third value=false}}
My strategy is to deserialize to Dictionary<string, object at first, worry about recursion later. Any suggestions (existing library?) appreciated!
Here is what I have
var stringContentTrimmed = stringContent.Substring(1, stringContent.Length - 2);
var objects = stringContentTrimmed.Split(',')
.Select(x => x.Trim())
.Where(x => !String.IsNullOrWhiteSpace(x));
TLDR. The Split function is also splitting up my values, which isn't what I want.
Really you need to have a proper specification or grammar for this but I'm going to take a wild guess and say that there isn't one, and if there was, actual values would not conform to it.
Your best bet might be:
Eliminate any whitespace adjacent to = { } or , characters
Replace any , with ","
Replace any = with "="
replace any { with {"
replace any } with "}
replace any "{ with {
replace any }" with }
replace any = with :
Then treat as JSON.
I tried this with your example and it worked. Whether it will work with your actual values I have no idea - this will depend on whether they stick to the restrictions you have described. If keys or values embed any of "{}:=, or if leading or trailing spaces are significant, then it will not work.
I created a method GetObjects below which returns a Dictionary<string, string> of the top-level objects and the raw content inside. Another method, Merge returns a nested dictionary by calling GetValues to extract the key-value pairs from the object content.
Using your example string, the Merge method returns this:
class Program
{
static void Main(string[] args)
{
var str = "{Foo={name=Foo Value, active=true, some date=20170630}, Bar={name#=My Bar Value}, Key With Space={name=Foo Bar, active=false}}";
var values = GetObjects(str);
Dictionary<string, Dictionary<string, string>> objects = Merge(values);
}
public static Dictionary<string, Dictionary<string, string>> Merge(Dictionary<string, string> input)
{
var output = new Dictionary<string, Dictionary<string, string>>();
foreach (var key in input.Keys)
{
var value = input[key];
var subValues = GetValues(value);
output.Add(key, subValues);
}
return output;
}
public static Dictionary<string, string> GetObjects(string input)
{
var objects = new Dictionary<string, string>();
var objectNames = new Queue<string>();
var objectBuffer = string.Empty;
foreach (var c in input)
{
if (char.Equals('{', c))
{
if (!string.IsNullOrEmpty(objectBuffer))
{
var b = objectBuffer.Trim('{', '}', ',', ' ', '=');
objectNames.Enqueue(b);
}
objectBuffer = string.Empty;
}
if (char.Equals('}', c))
{
if (objectNames.Count > 0)
{
var b = objectBuffer.Trim('{');
var key = objectNames.Dequeue();
objects.Add(key, b);
}
objectBuffer = string.Empty;
}
objectBuffer += c;
}
return objects;
}
private static Dictionary<string, string> GetValues(string input)
{
var output = new Dictionary<string, string>();
var values = input.Split(new string[] { ", " }, System.StringSplitOptions.None);
foreach (var val in values)
{
var parts = val.Split('=');
if (parts.Length == 2)
{
var key = parts[0].Trim(' ');
var value = parts[1].Trim(' ');
output.Add(key, value);
}
}
return output;
}
}

How can I sort (Custom Sort) list of Dictionary entry by value

My hashtable contains (key, Values[])
e.g:
myHashtable[keys, Values[]]
myHashtable.Add[1, Value1];
myHashtable.Add[2, Value2];
myHashtable.Add[3, Value3];
myHashtable.Add[4, Value4];
myHashtable.Add[5, Value5];
Where Value1; Value2, value3, value4, and value5 is as follows.
Value1[name = "Smith"]
Value1[Title= "Mr"]
Value1[Salary = 1000]
Value1[Identity = "S"]
Value2[name = "Peter"]
Value2[Title= "Mr"]
Value2[Salary = 1000]
Value2[Identity = "A"]
Value3[name = "Tom"]
Value3[Title= "Mr"]
Value3[Salary = 1000]
Value3[Identity = "C"]
Value4[name = "Marry"]
Value4[Title= "Ms"]
Value4[Salary = 1000]
Value4[Identity = ""]
Value5[name = "Sam"]
Value5[Title= "Mr"]
Value5[Salary = 1000]
Value5[Identity = "C"]
I want to order this dictionaryEntry list values where Identity with "C" values first then "A" then "S" then ""
After sorting the Result should be like as follows.
myHashtable.Add[3, Value3]; // Value3.Identity = "C"
myHashtable.Add[5, Value5]; // Value5.Identity = "C"
myHashtable.Add[2, Value2]; // Value2.Identity = "A"
myHashtable.Add[1, Value1]; // Value1.Identity = "S"
myHashtable.Add[4, Value4]; // Value4.Identity = ""
Here is my attempt.
var result1 = new List<DictionaryEntry>(hashtable.Count);
var result2 = new List<DictionaryEntry>(hashtable.Count);
var result3 = new List<DictionaryEntry>(hashtable.Count);
var result4 = new List<DictionaryEntry>(hashtable.Count);
var result = new List<DictionaryEntry>(hashtable.Count);
foreach (DictionaryEntry entry in hashtable)
{
result.Add(entry);
}
foreach (DictionaryEntry dictionaryEntry in result)
{
var t2 = dictionaryEntry.Value;
switch (t2.Identity)
{
case "C":
result1.Add(dictionaryEntry);
break;
case "A":
result2.Add(dictionaryEntry);
break;
case "S":
result3.Add(dictionaryEntry);
break;
case "":
result4.Add(dictionaryEntry);
break;
default:
break;
}
}
result1.ToList();
result2.ToList();
result3.ToList();
var combinedResult = result1.Union(result2)
.Union(result3)
.Union(result4)
.ToDictionary(k => k.Key, v => v.Value).OrderByDescending(v => v.Value);
How can I sort combinedResult to give me the above custom sorted dictionary entry list?
Any help is greatly appriciated.
Thank You
When a Dictionary data structure is implemented with a hashtable, in order to achieve the amortized O(1) insert/delete/update operations the data is unsorted. On the other hand, when the Dictionary is implemented with a balanced tree, the operations are a bit slower O(logn) but they can be enumerated in sorted way (by the key). For example, C# dictionary implementation is unsorted, and a C++ map is sorted (based on a red-black tree)
Given the above (you cannot have the data sorted however you want in the dictionary), What you can do is save the dictionary as a List/Array, and then sort by whichever comparator you want.
Here is an example of a Dictionary and a custom comparer, where you can get the values in the dictionary sorted by the logic in the custom comparer:
public class Data
{
public string Name { get; set; }
public string Identity { get; set; }
}
public class CustomerComparer : IComparer<KeyValuePair<int, Data>>
{
private List<string> orderedLetters = new List<string>() { "C", "A", "S" };
public int Compare(KeyValuePair<int, Data> str1, KeyValuePair<int, Data> str2)
{
return orderedLetters.IndexOf(str1.Value.Identity) - orderedLetters.IndexOf(str2.Value.Identity);
}
}
class Program
{
static void Main(string[] args)
{
Data value1 = new Data { Name = "Name1", Identity = "S" };
Data value2 = new Data { Name = "Name2", Identity = "A" };
Data value3 = new Data { Name = "Name3", Identity = "C" };
Data value4 = new Data { Name = "Name4", Identity = "C" };
Dictionary<int, Data> unsortedDictionary = new Dictionary<int, Data>();
unsortedDictionary.Add(1, value1);
unsortedDictionary.Add(2, value2);
unsortedDictionary.Add(3, value3);
unsortedDictionary.Add(4, value4);
var customSortedValues = unsortedDictionary.Values.OrderBy(item => item, new CustomerComparer()).ToArray();
for (int i=0; i < customSortedValues.Length; i++)
{
var kvp = customSortedValues[i];
Console.WriteLine("{0}: {1}=(Name={2}, Identity={3})", i, kvp.Key, kvp.Value.Name, kvp.Value.Identity);
}
}
}
//Output is:
//0: Name3=C
//1: Name4=C
//2: Name2=A
//3: Name1=S
You can also use a SortedDictionary (as #Clockwork-Muse suggests) and pass a similar CustomComparer as in the above example. This really depends on what your requirements are. If you need the operations to stay fast and only need the values sorted for, perhaps, reporting, then just sort when the values are needed (as in my example). If you will be accessing the sorted values alot, it may make sense to just keep them sorted in the first place.

safest way to parse a string and split it

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);
}
}

Mapping from 2D array in C#

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];
}
}

Categories