Multi string array to multi dictionary in C#? - c#

I got stuck to change the following array to a dictionary.
public static string[][,] patterns = new string[][,]
{
new string[,] {
{ "1,2,3" },
{ "3,2,5" },
},
new string[,] {
{ "4,4,3" },
{ "7,1,2" },
},
};
This is what I have:
public Dictionary<string, string[]> patterns = new Dictionary<string, string[]>();
I can't fill the array with predefined values.
I want to change to a dictionary, because it has a key.
Can I also change the above array to a key and values format?
I want something like this: { "keyNameExample1", "1,2,3", "4,5,6", "etc"}. I want do something like this: patterns["keyNameExample", 1 (integer array pack)]; or patterns["keyNameExample", 2]; (to get the second arrays)
{ "keyNameExample1", "1,2,3", "4,5,6", "etc"} { "keyNameExample2", "5,7,8", "1,1,1", "etc"} and get it like this: patterns["keyNameExample1", 2]; or patterns["keyNameExample2", 1];

can make it even shorter like:
public static Dictionary<string, string[]> demo = new Dictionary<string, string[]>
{
{ "abc", new[]{"1","2"}},
{ "def", new[]{"3","4"}},
};
and with C# 9 you can even do:
public static Dictionary<string, string[]> demo = new()
{
{ "abc", new[]{"1","2"}},
{ "def", new[]{"3","4"}},
};

You can just make it a dictionary with a list, and I guess that covers your requirements (index access, variable number of integers for the index element). Here is an example (for the value list I am not sure, whether you really want a string or ints, in case just change the type):
// define dictionary
IDictionary<string, IList<int>> dict = new Dictionary<string, IList<int>>();
// assign values
dict["abc"] = new List<int> { 2, 4, 8 };
dict["def"] = new List<int> { 10, 12, 14 };
// get value
int dictDef2 = dict["def"][1];

Finally, I got it.
public static Dictionary<string, string[]> demo = new Dictionary<string, string[]>
{
{ "abc",
new string[]
{
"1",
"2"
}
},
{ "def",
new string[]
{
"3",
"4"
}
},
};
Contains check:
if (demo.ContainsKey("abc"))
{
}
Get the value(s):
demo["abc"][0]
Thanks for any help.

If you want to change existing array, you can try Linq:
using System.Linq;
...
public static string[][,] patterns = new string[][,] {...}
...
public Dictionary<string, string[]> patternsDict = patterns
.Select((value, index) => (
key : $"keyNameExample{index}",
value : value.OfType<string>().ToArray()))
.ToDictionary(pair => pair.key, pair => pair.value);
Note, that we have to convert (flatten) 2d array into ordinal one
If you want just to declare dictionary and fill it:
public Dictionary<string, string[]> patterns = new Dictionary<string, string[]>() {
{"keyExample1", new string[] { "1,2,3", "3,2,5" }},
{"keyExample1", new string[] { "4,4,3", "7,1,2" }},
};

public string[][,] patterns = new string[][,] { new string[,] { { "1,2,3" }, { "3,2,5" } }, new string[,] { { "4,4,3" }, { "7,1,2" } } };
Dictionary<string, string[,]> patternsDictionary = new Dictionary<string, string[,]>();
for (int i = 0; i < patterns.Length; i++)
{
patternsDictionary.Add(i.ToString(), patterns[i]);
}
Console.WriteLine(patternsDictionary["0"][0,1]); // Returns 2 - from { "1,2,3" }

Related

how to call a dictionary with a string

I have 4 dictionary, each contain a button's name and the button's value.
I Have a List that contain the name of each dictionary
private Dictionary<string, int> TableArray = new Dictionary<string, int>() { { "ButtonRMT35", 35 }, { "ButtonRMT17", 17 }, { "ButtonRMT11", 11 }, { "ButtonRMT8", 8 }, { "ButtonRMT5", 5 } };
private Dictionary<string, int> ParArray = new Dictionary<string, int>() { { "ButtonRMP20", 20 }, { "ButtonRMP15", 15 }, { "ButtonRMP10", 10 }, { "ButtonRMP5", 5 } };
private Dictionary<string, int> MaxChipsRPArray = new Dictionary<string, int>() { { "ButtonRPC20", 20 }, { "ButtonRPC15", 15 }, { "ButtonRPC10", 10 }, { "ButtonRPC5", 5 } };
private Dictionary<string, int> QuestionSerieRPArray = new Dictionary<string, int>() { { "ButtonRPQ20", 20 }, { "ButtonRPQ15", 15 }, { "ButtonRPQ10", 10 }, { "ButtonRPQ5", 5 } };
public List<string> DictionaryList = new List<string>() { "TableArray", "ParArray", "MaxChipsRPArray", "QuestionSerieRPArray" };
I would like to do something like that
foreach (var dictionnary in DictionaryList)
{
foreach (var buttonName in dictionnary.Keys)
{
DoSomething();
}
}
Is there a way to do that?
The List<string> (DictionaryList) contains strings. Those strings are not variable identifiers (variable identifiers are not the same as C#/.NET strings; variable identifiers are lexical tokens in the C# language, being parsed by the C# compiler during the build of your program), and thus cannot be used to refer to some variable.[1]
Rather than maintaining strings in your DictionaryList, let it maintain the dictionaries itself:
private Dictionary<string, int> TableArray = ...
private Dictionary<string, int> ParArray = ...
private Dictionary<string, int> MaxChipsRPArray = ...
private Dictionary<string, int> QuestionSerieRPArray = ...
public List<Dictionary<string, int>> DictionaryList = new List<Dictionary<string, int>>()
{
TableArray, ParArray, MaxChipsRPArray, QuestionSerieRPArray
};
If you need to access the dictionaries by some name provided as a string (regardless whether that name would correlate with the variable/field names) you can turn the list into a dictionary of dictionaries (mapping some name to each of your dictionaries) instead:
private Dictionary<string, int> TableArray = ...
private Dictionary<string, int> ParArray = ...
private Dictionary<string, int> MaxChipsRPArray = ...
private Dictionary<string, int> QuestionSerieRPArray = ...
public Dictionary<string, <Dictionary<string, int>>> Dictionaries =
new Dictionary<string, <Dictionary<string, int>>>()
{
["TableArray"] = TableArray,
["MaxChipsRPArray"] = MaxChipsRPArray,
["QuestionSerieRPArray"] = QuestionSerieRPArray
};
...
foreach (var dictionary in Dictionaries.Values)
{
foreach (var buttonName in dictionary.Keys)
{
DoSomething();
}
}
You could then access an individual dictionary by name through the public Dictionaries field like this, for example:
var someDictionaryIWant = Dictionaries["MaxChipsRPArray"];
foreach (var buttonName in someDictionaryIWant.Keys)
{
DoSomething();
}
[1] I just told a grey lie here. For fields and properties, it would be possible to access fields/properties by their field/property name given as a string through a mechanism called "reflection". But reflection is cumbersome (likely even complicated for inexperienced programmers), slow, does not play well with trimming or compiling into native code, is normally not applicable to local variables declared inside methods, etc... But i think it's doubtful that you are looking for some dirty hack-ish way to solve your problem when there are cleaner and more straightforward solutions to your problem.
You are on a right track, however it would be hard to achieve by using variable names.
Use nested list, like this: List<Dictionary<string, int>> dictionaryList
Then add your dictionaries to the list, and iterate over them in the for each loop like you initially wanted to.

Do I really need to create and return the collection for this method?

I have seen people use yield return, although every time I try and use it in foreach it tells me something along the lines of: The body of CollectDictionary cannot be an iterator block because Dictionary<string, object> is not an interator interface type
I have a lot of methods line the following, is there a way to reduce boilerplate?
public Dictionary<string, object> CollectDictionary()
{
var configLines = File.ReadAllLines(_file).Where(IsValidConfigLine);
var configElements = new Dictionary<string, object>();
foreach (var configElement in configLines)
{
configElements.Add(configElement.Split("=")[0], configElement.Split("=")[1]);
}
return configElements;
}
You can reduce the amount of code by using .ToDictionary().
public Dictionary<string, object> CollectDictionary()
{
string[] configLines = new string[]
{
"foo1=bar1",
"foo2=bar2",
"foo3=bar3",
"foo4=bar4",
};
return configLines.Select(configElement => configElement.Split("="))
.ToDictionary(splt => splt[0], splt => (object)splt[1]);
}
This should return the following Dciotnary
{
{ "foo1", "bar1" },
{ "foo2", "bar2" },
{ "foo3", "bar3" },
{ "foo4", "bar4" }
}

Assigning Values to KeyValuePair

I want to assign some static values to my KeyValuePair object.
private IEnumerable<KeyValuePair<string, string>> getCountries()
{
return new List<KeyValuePair<string, string>>()
{
{ "code1", "value1" },
{ "code2", "value2" }
};
}
But this is throwing nooverloaded method error.
return new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("code1", "value1"),
new KeyValuePair<string, string>("code2", "value2"),
};
If you're using .NET Core 2.0+, you can use the slightly less verbose:
return new List<KeyValuePair<string, string>>()
{
KeyValuePair.Create("code1", "value1"),
KeyValuePair.Create("code2", "value2"),
};
In C# 9, you can use target-typed new to write this as:
return new List<KeyValuePair<string, string>>()
{
new("code1", "value1"),
new("code2", "value2"),
};
Or with Dictionary you can achieve desired initialization style
var pairs = new Dictionary<string, string>
{
{ "one", "first" },
{ "two", "second" },
}.ToList();
pairs.Should().BeOfType<List<KeyValuePair<string, string>>>(); // Pass
Notice, that if later in the code you are going to just enumerate list of key value pairs, then you can use dictionary without explicitly converting it to the list.
var pairs = new Dictionary<string, string>
{
{ "one", "first" },
{ "two", "second" },
}
// later somewhere in the code
foreach(var pair in pairs)
{
Console.WriteLine($"{pair.Key}: {pair.Value}")
}
If you are using values internally (inside class), you can use tuples.
private IEnumerable<(string Code, string Name)> GetCountries()
{
yield return ("code", "Earth");
yield return ("code", "Vulkan");
}
Which later can be consumed in more readable way
foreach(var country in GetCountries())
{
Console.WriteLine($"{country.Code}: {country.Name}")
}
If type used across application, then instead of using key-value pairs you can show intentions of your code to the readers of your code and create custom type.
public class Country
{
public string Code { get; }
public string Name { get; }
public Country(string code, string name)
{
Code = code;
Name = name;
}
}
private IEnumerable<Country> GetCountries()
{
yield return new Country("code", "Earth");
yield return new Country("code", "Vulkan");
}
Which later can be consumed in more readable way
foreach(var country in GetCountries())
{
Console.WriteLine($"{country.Code}: {country.Name}")
}
You need to consider that both Key and Value properties of the generic class are read-only, so you cannot set them directly. Instead you need to take advantage of the class's constructor in order to set the desired pairs.
public IEnumerable<KeyValuePair<string, string>> getCountries()
{
var keyValue1 = new KeyValuePair<string,string>("code1","value1");
var keyvalue2 = new KeyValuePair<string,string>("code2","value2");
var keyValueList = new List<KeyValuePair<string, string>> {keyValue1, keyvalue2};
return keyValueList;
}

Sort a list of dot notated namespace strings into a nested JSON string c#

How can I sort a list of dot notated namespace strings into a nested JSON string?
something like this.
var string1 = "example.string.1";
var string2 = "example.string.2";
var string3 = "example.anotherstring.1";
sorted into this.
{
"example": {
"string": ["1", "2"],
"anotherstring": ["1"]
}
}
EDIT 1
Thanks guys! I was able to get it CLOSE to working using your answers.
with this list it works:
var strings = new List<string>
{
"example.string.1",
"example.string.2",
"example.anotherstring.1",
};
public void Example()
{
var dict = new Dictionary<string, dynamic>();
foreach (var s in strings)
{
AddPartsToDictionary(dict, s.Split('.'));
}
}
public void AddPartsToDictionary(IDictionary<string, dynamic> dict, string[] parts)
{
for (var i = 0; i < parts.Length; i++)
{
if (i < parts.Length - 2)
{
if (!dict.ContainsKey(parts[i]))
dict.Add(parts[i], new Dictionary<string, dynamic>());
dict = dict[parts[i]];
}
else if (i < parts.Length - 1)
{
if (!dict.ContainsKey(parts[i]))
{
var list = new List<string>();
dict[parts[i]] = list;
}
}
else
{
var list = dict[parts[i - 1]] as List<string>;
list.Add(parts[i]);
}
}
}
result json
{
"example": {
"string": [
"1",
"2"
],
"anotherstring": [
"1"
]
}
}
with this list it fails
var strings = new List<string>
{
"example.string.1",
"example.string.example.1",
"example.string.2",
"example.anotherstring.1",
"example.string.example.2",
"string.example.2"
};
I might have to sort the list or something, still working on it. Hope this helps with figuring it out.
Objects All the Way Down
If using objects (instead of arrays) all the time is okay, then this will work.
var strings = new List<string> {
"example.string.1",
"example.string.2",
"example.anotherstring.1",
};
var result = strings.Aggregate(new Dictionary<string, Object>(), (acc, s) =>
{
var level = acc;
foreach(var segment in s.Split('.'))
{
if (!level.ContainsKey(segment))
{
var child = new Dictionary<string, Object>();
level.Add(segment, child);
}
level = level[segment] as Dictionary<string, Object>;
}
return acc;
});
var json = JsonConvert.SerializeObject(result, Formatting.Indented);
The output has objects all the way down.
{
"example": {
"string": {
"1": {},
"2": {}
},
"anotherstring": {
"1": {}
}
}
}
Arrays at the Last Level (with a Fiddle)
If you would like arrays at the last level, then we need a precise definition of what defines the last level. For instance, does the last level always contain only integers? If so the following works.
var result = strings.Aggregate(new Dictionary<string, Object>(), (acc, s) =>
{
Dictionary<string, Object> previousLevel = null;
Dictionary<string, Object> nextLevel = acc;
string previousSegment = null;
foreach (string nextSegment in s.Split('.'))
{
if (Int16.TryParse(nextSegment, out _))
{
if (previousLevel[previousSegment] is Dictionary<string, Object>)
{
previousLevel[previousSegment] = new List<string>();
}
var list = previousLevel[previousSegment] as List<string>;
list.Add(nextSegment);
}
else
{
if (!nextLevel.ContainsKey(nextSegment))
{
var child = new Dictionary<string, Object>();
nextLevel.Add(nextSegment, child);
}
previousSegment = nextSegment;
previousLevel = nextLevel;
nextLevel = nextLevel[nextSegment] as Dictionary<string, Object>;
}
}
return acc;
});
The output has arrays at the last level.
{
"example": {
"string": [
"1",
"2"
],
"anotherstring": [
"1",
"2"
]
}
}
This array version will work only if the last level is only of integer types, which is what your original question required.
The more difficult case is to handle mixed string and integer types at the same level, which would have an input that looks something like this:
"System.1",
"System.2",
"System.Collections.1",
"System.Collections.2",
"System.Collections.Generic.1"
That requires a more complex algorithm. If that is the hidden requirement, consider asking a brand new follow-up question and linking to it in a comment to this answer.
I thought id have a quick go at this, its close but not exactly what you wanted. However i thought it might inspire you or someone else to have a try.
Disclaimer, i'm not really a Json.net aficionado and this code is
fairly well.. unusual, to say the least
Demo Here
Given a list
var list = new List<string>
{
"example.string.1",
"example.string.2",
"example.anotherstring.1",
"example.anotherstring.2",
"System",
"System.Diagnostics",
"System.Text",
"System.Collections.Generic",
"System.Linq",
"System.Diagnostics.1",
"System.Text.1",
"System.Collections.Generic.1",
"System.Linq.1",
"System.Diagnostics.2",
"System.Text.2",
"System.Collections.Generic.2",
"System.Linq.2"
};
Some Classes
public class RawValue
{
public RawValue(string key, IEnumerable<IEnumerable<string>> values)
{
Key = key;
Values = values;
}
public string Key { get; set; }
public IEnumerable<IEnumerable<string>> Values { get; set; }
}
public class NameSpace : Dictionary<string, NameSpace>
{
}
Some Helpers
private static IEnumerable<RawValue> GetRawValues(RawValue value)
{
return value.Values.Where(x => x.Any())
.GroupBy(x => x.FirstOrDefault())
.Select(x => new RawValue(x.Key, x.Select(y => y.Skip(1))));
}
private static NameSpace GroupNameSpaces(IEnumerable<RawValue> groups)
{
var result = new NameSpace();
foreach (var group in groups)
result.Add(group.Key, GroupNameSpaces(GetRawValues(group)));
return result;
}
Main Code
var groups = list.Select(x => x.Split('.'))
.GroupBy(x => x.FirstOrDefault())
.Select(x => new RawValue(x.Key, x.Select(y => y.Skip(1))));
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
var result = GroupNameSpaces(groups);
var json = JsonConvert.SerializeObject(result, Formatting.Indented, settings);
Console.WriteLine(json);
Output
{
"example": {
"string": {
"1": {},
"2": {}
},
"anotherstring": {
"1": {},
"2": {}
}
},
"System": {
"Diagnostics": {
"1": {},
"2": {}
},
"Text": {
"1": {},
"2": {}
},
"Collections": {
"Generic": {
"1": {},
"2": {}
}
},
"Linq": {
"1": {},
"2": {}
}
}
}
I think my downfall was having to use NameSpace : Dictionary<string, NameSpace> due to the fact i didn't know how to better serialise in Json.Net. Also without spending time on this, i couldnt figure out how to convert the last node to an json array. though i'm not sure if you will be able to if you think about it. However i'm sure this can be done better

Dictionary.ContainsKey(entry) returns false

I have a string List that contains some numbers .
I have some string in mybuylist that matches in Dictionary list. But this if condition always returns False. Ok mybuylist like [34,45,58] and mcollection(key,value) like this {[565,5]},{[34,1]},{[78,9]}....
public static Dictionary<string, int> mcollection = new Dictionary<string, int>();
public static List<string> mybuylist = new List<string>();
foreach (string entry in mybuylist) {
if (mcollection.ContainsKey(entry))
{
//dosomething
}
}
Hope someone help me about this
It might be the the problem of case-sensitive comparison or Keys are not matching. All Dictionaries are case-sensitive by default. A and a are different. Verify whether the values in mybuylist and mcollection are same or not.
Declare mcollection like below.
public static Dictionary<string, int> mcollection = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
This will ignorecase and A & a are same.
See this.
EDIT 1:
class Program
{
public static Dictionary<string, int> mcollection = new Dictionary<string, int>() { { "565", 5 }, { "34", 1 }, { "78", 9 } };
public static List<string> mybuylist = new List<string>() { "34", "45", "58" };
static void Main(string[] args)
{
foreach (string entry in mybuylist)
{
if (mcollection.ContainsKey(entry))
{
Console.WriteLine(entry);
//dosomething
}
}
}
}
O/P: 34
EDIT 2: Use below code to remove the spaces
mybuylist = mybuylist.ConvertAll(s => s.Trim());

Categories

Resources