Modifying duplicate Key value on entry to Dictionary (C#) - c#

I have a requirement where I would like to use a dictionary to store "key=value" pairs, however now I need to store "duplicate" key values.
I have a line of delimited text (delimited with a pipe character ("|") which I split up into an array and then into a dictionary (see below)
string[] t = rawMessage.Split(new[] { '|', '|' }, StringSplitOptions.RemoveEmptyEntries);
message = t.ToDictionary(s => s.Split('=')[0], s => s.Split('=')[1]);
This works fine if no duplicates exist but I want to store duplicate keys in a way that adjusts the key value before insertion.
example
A=1|B=2|C=3|D=4|D=5|D=5
I want the key value to be adjusted to something like:
A=1
B=2
C=3
D=4
D[1]=5
D[2]=5
So then I can pull out the entry of the 2nd and 3rd "D" entries as they have a new key value.
Each duplicate is a unqiue record, as such I would like to be able to reference then in the order they were entered. the forth entry would be D[4] for example. A suggestion on using the same key value for all the values, may/will be confusing and I may end up pulling the wrong information (but will keep it in mind).
I would like to avoid having to loop through the array beforehand, and wondered if anyone knows a way I can perform this on the ToDictionary part of the code above.
Apologies for the fairly basic explaination.
Many of the threads deal with removing dupes in dictionaries and I understand that this is not the intended use of a dictionary.

If you want to associate multiple values with a key, you could just make the value part of the pair a list:
var dict = new Dictionary<char, List<int>>();
char key = 'D';
int value = 5;
if (!dict.ContainsKey(key))
dict[key] = new List<int>();
dict[key].Add(value);

You can use GroupBy to group the result set by the key, then flatten the groupings using SelectMany, projecting the desired key using the Select method overload with index parameter:
var message = t
.Select(s => s.Split('='))
.GroupBy(s => s[0], s => s[1])
.SelectMany(g => g.Select((v, i) => new
{
Key = i == 0 ? g.Key : g.Key + "[" + i + "]",
Value = v
}))
.ToDictionary(e => e.Key, e => e.Value);

You can either use a dictionary with a collection-based value:
Dictionary<string, List<int>>
You can then use the best data structure for the job you need, but List<> is a good default.
Or you can use the Lookup<TKey, TElement> class, but this doesn't give array indexing on the value elements:
Lookup<string, int>
Which supports groupings under the key (so an enumerable of TElement). The docs for it are here.
Alternatively still, roll your own data structure, but it is very unlikely this would be required - a composite of existing things will likely work for you well enough.

Well..I'm going to a basic answer,without using LINQ. What i would do is make my own ToDictionary method, just checking if a key exists and if it does,create the new key and add it to the Dictionary, something like this:
public Dictionary<string,string> ToDictionary(string[] t)
{
Dictionary<string, string> dict = new Dictionary<string, string>();
foreach (string s in t)
{
string[] reg = s.Split('=');
int i = 1;
while (dict.ContainsKey(reg[0]))
{
if (!reg[0].Contains('['))
reg[0] = reg[0] + "[" + i.ToString() + "]";
else
reg[0] = reg[0].Replace((i - 1).ToString(), i.ToString());
i++;
}
dict.Add(reg[0], reg[1]);
}
return dict;
}
This can easily be converted in an extension Method.

Alternative way would be to use NameValueCollection in System.Collections.Specialized. The interesting thing with NameValueCollection is that if there are duplicate values for the same key, it will keep the values in the same key as comma separated value. The key will be the same this way, but the values you might have to separate it out.
A quick sample and output is given below.
class Program
{
static void Main(string[] args)
{
NameValueCollection col = new NameValueCollection();
col.Add("A", "1");
col.Add("A", "2");
col.Add("B", "0");
col.Add("B", "1");
col.Add("B", "3");
col.Add("D", "1");
foreach (string key in col.AllKeys)
{
Console.WriteLine(key + " " + col[key] + "\n");
}
Console.ReadKey();
}
}
Output
A 1,2
B 0,1,2
D 1
Output

Like Adam Houldsworth said you can use Lookup.
Here is a snippet based on Adam Houldsworth's answer.
var str = #"A=1|B=2|C=3|D=4|D=5|D=5";
string[] t = str.Split(new[] { '|', '|' }, StringSplitOptions.RemoveEmptyEntries);
var message = t.ToLookup(s => s.Split('=')[0], s => Convert.ToInt32(s.Split('=')[1]));
var res = message.ToDictionary(i=>i.Key, x=>x.LastOrDefault());
Edit:
After reading your question again I understand you want the D=4 value also.
var res = message.SelectMany(i => i.Select((x, y) => new
{
Key = i.Key,
Value = x
})).GroupBy(q=>q.Value)
.SelectMany(x=>x.ToLookup(xx=>xx.Key, xx=>xx.Value));

Related

Adding more than one value to a single key in a hashtable in c#?

I'm trying to make a program where it reads strings that has a word and its meaning, for example
Book: Cover with Papers in between
Book: Reserve
And whenever I try my code I get an error because each key has to be unique. Is there a way to work around this?
Hashtable ht = new Hashtable();
var fileStream = new FileStream(#"e:\test.txt", FileMode.Open, FileAccess.Read);
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
string line;
while ((line = streamReader.ReadLine()) != null)
{
ht.Add(line.Split(':')[0], line.Split(':')[1]);
}
}
if (ht.ContainsKey("Book"))
{
listBox1.Items.Add(ht["Book"].ToString());
}
In the general case, you could use a List<string> for the value, and just Add to it. However, you can probably simplify with LINQ via ToLookup:
var groups = File.ReadLines(path)
.Select(line => line.Split(':'))
.ToLookup(x => x[0], x => x[1].Trim());
Now you can access groups[key] which gives you all the values with that prefix, or you can foreach over groups to get each combination of .Key and values.
In terms of your code, this is:
var groups = File.ReadLines(#"e:\test.txt")
.Select(line => line.Split(':'))
.ToLookup(x => x[0], x => x[1].Trim());
foreach(var val in groups["Book"])
listBox1.Items.Add(val);
(no need to check for existence first, it just works correctly if no match)
However! You only need to do this if you still want all the values after this code, i.e. you use groups somewhere else. If you don't, you can be more frugal and just abandon the unwanted data:
var values = File.ReadLines(#"e:\test.txt")
.Where(line => line.StartsWith("Book:"))
.Select(line => line.Substring(5).Trim());
foreach(var val in values)
listBox1.Items.Add(val);
Edit: minor thing - a vexing method signature means that line.Split(':') actually creates an array every time, because params; so I usually use:
static readonly char[] Colon = {':'};
and
line.Split(Colon)
Which is measurably more efficient if it is a hot path.
Use a Dictionary where the values is a list of strings:
var myDicitonary = new Dictionary<string, List<string>>
And now, you'd do the following:
if (!myDictionary.ContainsKey(key))
{
myDicitonary.Add(key, new List<string>());
}
myDicitonary[key].Add(value);
Use a Dictionary<string, List<string>> instead of the Hashtable.
Depending on what you want to achieve, you may use a SortedList or a SortedDictionary, that you initialze with your own IComparer, which allows duplicate keys.
Have a look at these Stackoverflow posts for details:
C# Sortable collection which allows duplicate keys
Is there an alternative to Dictionary/SortedList that allows duplicates?
The drawback of this solution is, that you cannot access the elements by key, but still by index.
use Dictionary instead.
Dictionary<string, list<string>> dic = new Dictionary<string, list<string>();

C#, Linq Search Dictionary in C# and return string using key

I have dictionary created as
Dictionary<string, string> delta = new dictionary<string, string>();
delta.Add("A", "One");
delta.Add("B", "Two");
delta.Add("C", "Three");
I wanted to retrieve value based on the value passed as key
public string GetValuefromdictionary(string roll. Dictionary<string, string> delta)
{
string rollValue;
return rollValue = delta
.Where(d => d.Key.Contains(roll))
.Select(d => d.Value)
.ToString();
}
however I am seeing it doesn't return me the string and I get like this
System.Linq.Enumerable+WhereSelectEnumerableIterator``2[System.Collections.Generic.KeyValuePair``2[System.String,System.String],System.String]
any help
if a Key is "ABC", and you pass "A" as roll, and you want to return the value from "ABC" because "ABC" contains "A". You can do this:
return delta.FirstOrDefault(d=> d.Key.Contains(roll)).Value;
Two options, better I would say two cases.
Case 1
Let's say Dictionary Values are unique, in this case we can simply transpose the dictionary swapping <Key,Value> pair.
var transformDictionary = delta.ToDictionary(kp => kp.Value, kp => kp.Key);
Now we can access this like any other dictioanry.
transformDictionary["One"];
Case 2 :
Values are not unique, in this case use LookUp.
var lookup = delta.ToLookup(c=>c.Value, c=>c.Key);
var lookupvalue = ((IEnumerable<string>)lookup["One"]).First();
Working Demo
You want to search for an element in a Dictionary, but using value instead of > a key. So you iterate through you dictionary and return the
first element that has the value you are looking for. The output you
get is a keyValue pair, hence the .Key
string key = "One";
string value = delta.FirstOrDefault(d => d.Value.Contains(key)).Key;

How to get value from dictionary without knowing key?

I want to get value from dictionary but i don't know key(Because dynamic generate dictionary from database) how can i get dictionary value.
If you some idea share me ...
For Example my database string value like
string jsonString = " "FB": "[{\"title\":\"sheet1\",\"rows\":[{\"height\":\"undefined\",\"columns\":[{\"value\":\"Cover Group \"},{\"value\":\"Sample Variable\"},{\"value\":\"Coverpoint Name\"},{\"value\":\"Crossed cover points\"},{\"value\":\"Coverpoint Comment\"},{\"value\":\"Bin Type\"},{\"value\":\"Bin Id\"},{\"value\":\"Sample Value\"},{\"value\":\"Expected Bin Count\"},{\"value\":\"Set Max Bin\"},{\"value\":\"Not Used\"}]},{\"height\":\"undefined\",\"columns\":[{\"value\":\"allCg,allSi\"},{\"value\":\"exSingle\"},{\"value\":\"exSingle\"},{},{\"value\":\"Example for single bin\"},{\"value\":\"single\"},{\"value\":\"valZero\"},{\"value\":\"1'b0\"},{\"formula\":\"1\",\"value\":1},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{},{},{},{},{\"value\":\"single\"},{\"value\":\"valOne\"},{\"value\":\"1'b1\"},{\"formula\":\"1\",\"value\":1},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{\"value\":\"ex1Bus[3:0]\"},{\"value\":\"exMulti\"},{},{\"value\":\"Example for multibin\"},{\"value\":\"multi\"},{},{\"value\":\"[0:15]\"},{\"formula\":\"16\",\"value\":16},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{},{\"value\":\"exCross\"},{\"value\":\"exSingle,exMulti\"},{\"value\":\"Example for cross\"},{\"value\":\"Implicit\"},{},{},{\"formula\":\"32\",\"value\":32},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{\"value\":\"ex2Bus[15:0]\"},{\"value\":\"exWildcard\"},{},{\"value\":\"example for wildcard\"},{\"value\":\"wildcard\"},{\"value\":\"ex_wildcard\"},{\"value\":\"16'bxxxxxxxxxxxxxx1\"},{\"formula\":\"1\",\"value\":1},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{\"value\":\"ex3Bus[4:0]\"},{\"value\":\"exImplicit\"},{},{\"value\":\"example for implicit & set max bin\"},{\"value\":\"Implicit\"},{},{},{\"formula\":\"8\",\"value\":8},{\"formula\":\"8\",\"value\":8},{}]},{\"height\":\"undefined\",\"columns\":[{},{\"value\":\"ex4Bus[3:0]\"},{\"value\":\"ex4Bus\"},{},{\"value\":\"setup for ignore example\"},{\"value\":\"multi\"},{},{\"value\":\"[0:15]\"},{\"formula\":\"16\",\"value\":16},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{},{\"value\":\"exIgnore\"},{\"value\":\"exSingle,ex4Bus\"},{\"value\":\"example for ignore\"},{\"value\":\"ignore\"},{},{\"value\":\"ex4Bus([12:15])\"},{\"formula\":\"24\",\"value\":24},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{\"value\":\"ex5Bus[3:0]\"},{\"value\":\"exIllegal\"},{},{\"value\":\"example for illegal\"},{\"value\":\"illegal\"},{},{\"value\":\"[12:15]\"},{\"formula\":\"16\",\"value\":16},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{},{},{},{},{},{},{},{},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{},{},{},{},{},{},{},{},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{},{},{},{},{},{},{},{},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{},{},{},{},{},{},{},{},{},{}]},{\"height\":\"undefined\",\"columns\":[{},{},{},{},{},{},{},{},{},{},{}]}],\"metadata\":{\"widths\":[\"200\",\"200\",\"200\",\"200\",\"200\",\"200\",\"200\",\"200\",\"200\",\"200\",\"200\"],\"frozenAt\":{\"row\":0,\"col\":0}}}]""
FB is dynamic key and after it's value title all value i need
If you don't have the key, but have the value and trying to get hold of the key, you can do this:
Dictionary<string, string> testData = new Dictionary<string, string>();
testData.Add("name", "Latheesan");
testData.Add("age", "26");
KeyValuePair<string, string> searchResult
= testData.FirstOrDefault(s => s.Value == "Latheesan");
string key = searchResult.Key; // returns "name" here
To get a sequence of all the Key/Value pairs where the value matches a target:
var dict = new Dictionary<string, int>
{
{"One", 1},
{"Two", 2},
{"Another One", 1},
{"Three", 3},
{"Yet Another One", 1}
};
int target = 1; // For example.
var matches = dict.Where(item => item.Value == target);
foreach (var kvp in matches)
Console.WriteLine("Key = " + kvp.Key);
The sample data you posted isn't a flat key-value dictionary. It contains embedded dictionaries - the base dictionary contains a title and a rows, which in turn consists of height and columns and so on, and at some point are key-value pairs who keys are, confusingly, named 'value'. Are you asking how to parse this data structure to get all the values whose key is value?
What you first need to do, since this appears to be a JSON-formatted entry, is parse the JSON into a .NET data structure, using libraries like JSON.NET or System.Web.Helpers.Json. These libraries will convert the JSON string into a hierarchy of dictionaries, all of them implementing IEnumerable, so you can iterate over it, more or less like this (this is not compilable code, just a demonstration!):
public void Main()
{
var jsonObject = Json.Decode(FB); // FB is your JSON string.
var values = new List<string>();
FindValues(jsonObject);
}
public void FindValues(jsonObject, values)
{
foreach (var child in jsonObject)
{
if (child.key == 'value')
{
values.Add(child.value);
}
// Recursively call FindValues on child objects.
FindValues(child, values);
}
}
This C#-ish pseudo-code shows you how to go over a dictionary, then optionally drill down deeper into internal dictionaries.
This code use for get value from dictionary value without knowing key and value..
var json = Newtonsoft.Json.JsonConvert.SerializeObject(jsonString );
var javSer = new JavaScriptSerializer();
var dfi = javSer.Deserialize<Dictionary<string, string>>(json);
string dataString= dfi.Values.First();
How can you possibly know which value you need if you don't have the key?
A Dictionary in .NET does contain a Keys and Values collection, so if you are only interested in the values, you can use that.

C# Convert List<string> to Dictionary<string, string>

This may seem an odd thing to want to do but ignoring that, is there a nice concise way of converting a List<string> to Dictionary<string, string> where each Key Value Pair in the Dictionary is just each string in the List. i.e.
List = string1, string2, string3
Dictionary = string1/string1, string2/string2, string3/string3
I have done plenty of searching and there are literally dozens of examples on Stackoverflow alone of doing it in the opposite direction but not this way round.
The reason for doing this is I have two third part components and changing them is out of my hands. One returns a list of email addresses as a List<string> and the other send emails where the To parameter is a Dictionary<string, string>. The key of the dictionary is the email address and the value is their real name. However, I don't know the real name but it still works if you set the real name to the email address as well. Therefore why I want to convert a List to a Dictionary<string, string>. There are plenty of ways of doing this. A foreach loop on the list which adds a kvp to a dictionary. But I like terse code and wondered if there was a single line solution.
Try this:
var res = list.ToDictionary(x => x, x => x);
The first lambda lets you pick the key, the second one picks the value.
You can play with it and make values differ from the keys, like this:
var res = list.ToDictionary(x => x, x => string.Format("Val: {0}", x));
If your list contains duplicates, add Distinct() like this:
var res = list.Distinct().ToDictionary(x => x, x => x);
EDIT To comment on the valid reason, I think the only reason that could be valid for conversions like this is that at some point the keys and the values in the resultant dictionary are going to diverge. For example, you would do an initial conversion, and then replace some of the values with something else. If the keys and the values are always going to be the same, HashSet<String> would provide a much better fit for your situation:
var res = new HashSet<string>(list);
if (res.Contains("string1")) ...
Use this:
var dict = list.ToDictionary(x => x);
See MSDN for more info.
As Pranay points out in the comments, this will fail if an item exists in the list multiple times.
Depending on your specific requirements, you can either use var dict = list.Distinct().ToDictionary(x => x); to get a dictionary of distinct items or you can use ToLookup instead:
var dict = list.ToLookup(x => x);
This will return an ILookup<string, string> which is essentially the same as IDictionary<string, IEnumerable<string>>, so you will have a list of distinct keys with each string instance under it.
EDIT
another way to deal with duplicate is you can do like this
var dic = slist.Select((element, index)=> new{element,index} )
.ToDictionary(ele=>ele.index.ToString(), ele=>ele.element);
or
easy way to do is
var res = list.ToDictionary(str => str, str=> str);
but make sure that there is no string is repeating...again otherewise above code will not work for you
if there is string is repeating than its better to do like this
Dictionary<string,string> dic= new Dictionary<string,string> ();
foreach(string s in Stringlist)
{
if(!dic.ContainsKey(s))
{
// dic.Add( value to dictionary
}
}
By using ToDictionary:
var dictionary = list.ToDictionary(s => s);
If it is possible that any string could be repeated, either do a Distinct call first on the list (to remove duplicates), or use ToLookup which allows for multiple values per key.
You can use:
var dictionary = myList.ToDictionary(x => x);

How to convert a 2-d array into a dictionary object

I have an array of type string that looks like this:
"test1|True,test2|False,test3|False,test4|True".
This is essentially a 2d array like so
[test1][True]
[test2][False]
[test3][False]
[test4][True].
I want to convert this into a dictionary<string,bool> using linq, something like:
Dictionary<string, bool> myResults = results.Split(",".ToCharArray).ToDictionary()
any ideas?
var d = results.Split(',')
.Select(row => row.Split('|'))
.ToDictionary(srow => srow[0], srow => bool.Parse(srow[1]));
First turn your string into a proper array:
String sData = "test1|True,test2|False,test3|False,test4|True";
String[] sDataArray = sData.Split(',');
Then you can process the String[] into a dictionary:
var sDict = sDataArray.ToDictionary(
sKey => sKey.Split('|')[0],
sElement => bool.Parse(sElement.Split('|')[1])
);
The ToDictionary method takes 2 functions which extract the key and element data from the each source array element.
Here, I've extracted each half by splitting on the "|" and then used the first half as the key and the second I've parsed into a bool to use as the element.
Obviously this contains no error checking so could fail if the source string wasn't comma separated, or if each element wasn't pipe separated. So be careful with where your source string comes from. If it doesn't match this pattern exactly it's going to fail so you'll need to do some tests and validation.
Marcelo's answer is similar, but I think it's a bit more elegant.
You could try something like this:
var resultsArray = results.Split(',');
var myResults = new Dictionary<string, bool>();
foreach (var str in resultsArray)
{
var parts = str.Split('|');
myResults.Add(parts[0], bool.Parse(parts[1]));
}
Something like this should work:
var fullString = "Test,True|Test2,False";
var query = from item in fullString.Split('|')
let splitted = item.Split(',')
let value = bool.Parse(splitted[1])
select new { Key = splitted[0], Value = value };
var dict = query.ToDictionary(pair => pair.Key, pair => pair.Value);

Categories

Resources