Convert string to Dictionary<string,byte>() - c#

I have a string that is formatted like this (the string is one continuous, no new line return)
/CallDump/CallInfo/KVP[#Key='Group' and (#Value='Best Group')]:10,
/CallDump/CallInfo/child::KVP[#Key='Dept' and (#Value='Customer Service' or #Value='Sales')]:240,
compare(Recordings/Recording/Location, 'New York')=0:20,
default:5,
I cannot seem to find a non complex way to convert it into a dictionary()
results would be something like this:
Key: /CallDump/CallInfo/KVP[#Key='Group' and (#Value='Best Group')] Value: 10
Key: compare(Recordings/Recording/Location, 'New York')=0 Value: 20

You can easily do this with Regex and LINQ:
var input = #"/CallDump/CallInfo/KVP[#Key='Group' and (#Value='Best Group')]:10,
/CallDump/CallInfo/child::KVP[#Key='Dept' and (#Value='Customer Service' or #Value='Sales')]:240,
compare(Recordings/Recording/Location, 'New York')=0:20,
default:5,";
var expression = new Regex(#"(.+):(\d{1,3})");
var result = input.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.Select(x => expression.Match(x))
.Select(m => new { Key = m.Groups[1].Value, Value = byte.Parse(m.Groups[2].Value) })
.ToDictionary(x => x.Key, x => x.Value);
Returns Dictionary<string, byte> with four elements.

Related

Identifying and grouping similar items in a collection of strings

I have a collection of strings like the following:
List<string> codes = new List<string>
{
"44.01", "44.02", "44.03", "44.04", "44.05", "44.06", "44.07", "44.08", "46", "47.10"
};
Each string is made up of two components separated by a full stop - a prefix code and a subcode. Some of the strings don't have sub codes.
I want to be able combine the strings whose prefixes are the same and output them as follows with the other codes also:
44(01,02,03,04,05,06,07,08),46,47.10
I'm stuck at the first hurdle of this, which is how to identify and group together the codes whose prefix values are the same, so that I can combine them into a single string as you can see above.
You can do:
var query = codes.Select(c =>
new
{
SplitArray = c.Split('.'), //to avoid multiple split
Value = c
})
.Select(c => new
{
Prefix = c.SplitArray.First(), //you can avoid multiple split if you split first and use it later
PostFix = c.SplitArray.Last(),
Value = c.Value,
})
.GroupBy(r => r.Prefix)
.Select(grp => new
{
Key = grp.Key,
Items = grp.Count() > 1 ? String.Join(",", grp.Select(t => t.PostFix)) : "",
Value = grp.First().Value,
});
This is how it works:
Split each item in the list on the delimiter and populate an anonymous type with Prefix, Postfix and original value
Later group on Prefix
after that select the values and the post fix values using string.Join
For output:
foreach (var item in query)
{
if(String.IsNullOrWhiteSpace(item.Items))
Console.WriteLine(item.Value);
else
Console.WriteLine("{0}({1})", item.Key, item.Items);
}
Output would be:
44(01,02,03,04,05,06,07,08)
46
47.10
Try this:-
var result = codes.Select(x => new { SplitArr = x.Split('.'), OriginalValue = x })
.GroupBy(x => x.SplitArr[0])
.Select(x => new
{
Prefix= x.Key,
subCode = x.Count() > 1 ?
String.Join(",", x.Select(z => z.SplitArray[1])) : "",
OriginalValue = x.First().OriginalValue
});
You can print your desired output like this:-
foreach (var item in result)
{
Console.Write("{0}({1}),",item.Prefix,item.subCode);
}
Working Fiddle.
Outlined idea:
Use Dictionary<string, List<string>> for collecting your result
in a loop over your list, use string.split() .. the first element will be your Dictionary key ... create a new List<string> there if the key doesn't exist yet
if the result of split has a second element, append that to the List
use a second loop to format that Dictionary to your output string
Of course, linq is possible too, e.g.
List<string> codes = new List<string>() {
"44.01", "44.05", "47", "42.02", "44.03" };
var result = string.Join(",",
codes.OrderBy(x => x)
.Select(x => x.Split('.'))
.GroupBy(x => x[0])
.Select((x) =>
{
if (x.Count() == 0) return x.Key;
else if (x.Count() == 1) return string.Join(".", x.First());
else return x.Key + "(" + string.Join(",", x.Select(e => e[1]).ToArray()) + ")";
}).ToArray());
Gotta love linq ... haha ... I think this is a monster.
You can do it all in one clever LINQ:
var grouped = codes.Select(x => x.Split('.'))
.Select(x => new
{
Prefix = int.Parse(x[0]),
Subcode = x.Length > 1 ? int.Parse(x[1]) : (int?)null
})
.GroupBy(k => k.Prefix)
.Select(g => new
{
Prefix = g.Key,
Subcodes = g.Where(s => s.Subcode.HasValue).Select(s => s.Subcode)
})
.Select(x =>
x.Prefix +
(x.Subcodes.Count() == 1 ? string.Format(".{0}", x.Subcodes.First()) :
x.Subcodes.Count() > 1 ? string.Format("({0})", string.Join(",", x.Subcodes))
: string.Empty)
).ToArray();
First it splits by Code and Subcode
Group by you Code, and get all Subcodes as a collection
Select it in the appropriate format
Looking at the problem, I think you should stop just before the last Select and let the data presentation be done in another part/method of your application.
The old fashioned way:
List<string> codes = new List<string>() {"44.01", "44.05", "47", "42.02", "44.03" };
string output=""
for (int i=0;i<list.count;i++)
{
string [] items= (codes[i]+"..").split('.') ;
int pos1=output.IndexOf(","+items[0]+"(") ;
if (pos1<0) output+=","+items[0]+"("+items[1]+")" ; // first occurence of code : add it
else
{ // Code already inserted : find the insert point
int pos2=output.Substring(pos1).IndexOf(')') ;
output=output.Substring(0,pos2)+","+items[1]+output.Substring(pos2) ;
}
}
if (output.Length>0) output=output.Substring(1).replace("()","") ;
This will work, including the correct formats for no subcodes, a single subcode, multiple subcodes. It also doesn't assume the prefix or subcodes are numeric, so it leaves leading zeros as is. Your question didn't show what to do in the case you have a prefix without subcode AND the same prefix with subcode, so it may not work in that edge case (44,44.01). I have it so that it ignores the prefix without subcode in that edge case.
List<string> codes = new List<string>
{
"44.01", "44.02", "44.03", "44.04", "44.05", "44.06", "44.07", "44.08", "46", "47.10"
};
var result=codes.Select(x => (x+".").Split('.'))
.Select(x => new
{
Prefix = x[0],
Subcode = x[1]
})
.GroupBy(k => k.Prefix)
.Select(g => new
{
Prefix = g.Key,
Subcodes = g.Where(s => s.Subcode!="").Select(s => s.Subcode)
})
.Select(x =>
x.Prefix +
(x.Subcodes.Count() == 0 ? string.Empty :
string.Format(x.Subcodes.Count()>1?"({0})":".{0}",
string.Join(",", x.Subcodes)))
).ToArray();
General idea, but i'm sure replacing the Substring calls with Regex would be a lot better as well
List<string> newCodes = new List<string>()
foreach (string sub1 in codes.Select(item => item.Substring(0,2)).Distinct)
{
StringBuilder code = new StringBuilder();
code.Append("sub1(");
foreach (string sub2 in codes.Where(item => item.Substring(0,2) == sub1).Select(item => item.Substring(2))
code.Append(sub2 + ",");
code.Append(")");
newCodes.Add(code.ToString());
}
You could go a couple ways... I could see you making a Dictionary<string,List<string>> so that you could have "44" map to a list of {".01", ".02", ".03", etc.} This would require you processing the codes before adding them to this list (i.e. separating out the two parts of the code and handling the case where there is only one part).
Or you could put them into a a SortedSet and provide your own Comparator which knows that these are codes and how to sort them (at least that'd be more reliable than grouping them alphabetically). Iterating over this SortedSet would still require special logic, though, so perhaps the Dictionary to List option above is still preferable.
In either case you would still need to handle a special case "46" where there is no second element in the code. In the dictionary example, would you insert a String.Empty into the list? Not sure what you'd output if you got a list {"46", "46.1"} -- would you display as "46(null,1)" or... "46(0,1)"... or "46(,1)" or "46(1)"?

C# File to Dictionary, but taking pairs of words

I am thinking about making a dictionary that contains words pairs as well as single words from a file.
Standard "single word" looks like:
private Dictionary<string, int> tempDict = new Dictionary<string, int>();
private void GetWords(string[] file)
{
tempDict = file
.SelectMany(i => File.ReadLines(i)
.SelectMany(line => line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)))
.GroupBy(word => word)
.ToDictionary(g => g.Key, g => g.Count());
}
And the string:
Adam likes coffee
will be:
Adam ; likes ; coffee
But I want to make it so it matches pairs as well (but only the neighbouring ones) so it would look like:
Adam ; Adam likes ; likes ; likes coffee ; coffee
I am not sure if it is manageable to do, and need some help with this one.
MoreLINQ has a Enumerable.Pairwise which takes the current and the predecessor value and a projections function.
Returns a sequence resulting from applying a function to each element in the source sequence and its predecessor, with the exception of the first element which is only returned as the predecessor of the second element.
Concatenating that with the original split value array would output:
var sentence = "Adam likes coffee";
var splitWords = sentence.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var pairWise = splitWords.Pairwise((first, second) => string.Format("{0} {1}", first,
second))
.Concat(splitWords)
.GroupBy(x => x)
.ToDictionary(x => x.Key, x => x.Count())
Would result in:

Build a new dictionary with concatenated duplicated keys based on values

How to concatenate and remove duplicates in dictionary like this:
Item1: Key=1, Value=test1
Item2: Key=2, Value=test2
Item3: Key=3, Value=test1
Item4: Key=4, Value=test3
Item5: Key=5, Value=test4
To build a new Dictionary like this:
Item1: Key=1-3, Value=test1
Item2: Key=2, Value=test2
Item4: Key=4, Value=test3
Item5: Key=5, Value=test4
I managed to get the duplicates using this: myDictionary.GroupBy(x => x.Value).Where(x => x.Count() > 1);
But I can't figure the right way to build a new Dictionary from this.
This works for taking all the duplicate items into a new dictionary with a shared key:
var dict = new Dictionary<string, string>
{
{"1", "test1"},
{"2", "test2"},
{"3", "test1"}
};
var groupedKeyMap = dict.GroupBy(x => x.Value)
.Where(x => x.Count() > 1)
.ToDictionary(x => string.Join("-", x.Select(y => y.Key)),
x => x.Key);
If you need both duplicate and non duplicate key value pairs, remove the Where clause:
var groupedKeyMap = dict.GroupBy(x => x.Value)
.ToDictionary(x => string.Join("-", x.Select(y => y.Key)),
x => x.Key);

Using Dictionary to count the number of appearances

My problem is that I am trying to take a body of text from a text box for example
"Spent the day with "insert famous name" '#excited #happy #happy"
then I want to count how many times each hashtag appears in the body, which can be any length of text.
so the above would return this
excited = 1
happy = 2
I Was planning on using a dictionary but I am not sure how I would implement the search for the hashtags and add to the dictionary.
This is all I have so far
string body = txtBody.Text;
Dictionary<string, string> dic = new Dictionary<string, string>();
foreach(char c in body)
{
}
thanks for any help
This can be achieved with a couple of LINQ methods:
var text = "Spent the day with <insert famous name> #excited #happy #happy";
var hashtags = text.Split(new[] { ' ' })
.Where(word => word.StartsWith("#"))
.GroupBy(hashtag => hashtag)
.ToDictionary(group => group.Key, group => group.Count());
Console.WriteLine(string.Join("; ", hashtags.Select(kvp => kvp.Key + ": " + kvp.Value)));
This will print
#excited: 1; #happy: 2
This will find any hashtags in a string of the form a hash followed by one or more non-whitespace characters and create a dictionary of them versus their count.
You did mean Dictionary<string, int> really, didn't you?
var input = "Spent the day with \"insert famous name\" '#excited #happy #happy";
Dictionary<string, int> dic =
Regex
.Matches(input, #"(?<=\#)\S+")
.Cast<Match>()
.Select(m => m.Value)
.GroupBy(s => s)
.ToDictionary(g => g.Key, g => g.Count());

Convert String list to a dictionary

I Have a string list like this ["saman=1", "kaman=2"]
How may I convert this to a dictionary like {Saman:1 , kaman:2}
strList.Select(k,v =>new {k,v} , k=> k.split('=')[0], val => v.split('=')[1]);
This should work:
strList.ToDictionary(x => x.Split('=')[0], x => x.Split('=')[1])
If you want Dictionary<string, int> you can parse the Value to integer:
strList.ToDictionary(x => x.Split('=')[0], x => int.Parse(x.Split('=')[1]))
You should split by ", " first, and then split each item by = to get key/value pairs.
Additional Trim call will get rid of [" at the beginning and "] at the end of your input string.
var input = #"[""saman=1"", ""kaman=2""]";
var dict = input.Trim('[', '"', ']')
.Split(new [] {#""", """}, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split('='))
.ToDictionary(x => x[0], x => x[1]);
Very, very simply with LINQ:
IDictionary<string, string> dictionary =
list.ToDictionary(pair => pair.Key, pair => pair.Value);
Note that this will fail if there are any duplicate keys - I assume that's okay?

Categories

Resources