How to populate KeyValuePair<int, int> inside a foreach loop - c#

I have this code snippet as below which iterates over a split string.
if (!string.IsNullOrEmpty(profile.ContactNumber))
{
var splitContract = profile.ContactNumber.Split(new string[] { "and", "&" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var contract in splitContract)
{
//check the split if it contains "x" or "X" character - if it does contain, it means it's a valid contract
if (contract.Contains("x") || contract.Contains("X"))
{
var condensedString = contract.Replace(" ", "");
var split = condensedString.Split(new char[] { 'x', 'X' });
GetNumbersOnly(split);
}
}
}
private void GetNumbersOnly(string[] inputArray)
{
var ListKeyValuePair = new List<KeyValuePair<string, string>>();
foreach (var item in inputArray)
{
var numberToAdd = Regex.Replace(item, "[^0-9]", "", RegexOptions.None);
ListKeyValuePair.Add(?, ?);
}
}
In GetNumbersOnly method, how can I populate List of KeyValuePair inside the for each loop?
The inputArray variable has an array element of [0] = 100, [1] = 5 for the first iteration and so on.
This is the desired output for the KeyValuePair {100, 5}, {200, 10}, {500, 15}.
Sorry, I can't seem to find any related scenario when I googled it. Any help with this is greatly appreciated.

Because the key and value are stored in separate array items, your logic is dependent on order. In cases like this, you should avoid for...each and instead use plain old for, which allows you to control the manner of iteration.
private void GetNumbersOnly(string[] inputArray)
{
var ListKeyValuePair = new List<KeyValuePair<string, string>>();
for (int i=0; i< inputArray.Length; i+=2) //The "2" here is very important!
{
var numberToAdd1 = Regex.Replace(inputArray[i], "[^0-9]", "", RegexOptions.None);
var numberToAdd2 = Regex.Replace(inputArray[i+1], "[^0-9]", "", RegexOptions.None);
ListKeyValuePair.Add(new KeyValuePair<string, string>(numberToAdd1, numberToAdd2));
}
}

The ListKeyValuePair.Add( ) function is expecting 1 field which is of type KeyValuePair. You need to make one of these with a new KeyValuePair() { key = item, value = numberToAdd };
Why are you keeping key value pairs in a list? Why not a Dictionary ? Do you want duplicate pairs?

Related

Convert string to dictionary key value - split issue

I am trying to convert a string to key value pair for testing purposes. The issue I have is that when I split the string, the value can be null in rare occasions
For example:
"Sent On\r\n2021-01-31 09:18:42"
"Priority\r\nLow"
When I use the following code it works fine for all records except when value is null. There will always be a key
Dictionary<string, string> details = new Dictionary<string, string>();
foreach (var row in Rows)
{
var text = row.Text.Replace("\r\n", ",");
var splitText = text.IndexOf(",");
var key = text.Substring(0, splitText);
var value = text.Substring(splitText + 1);
details.Add(key, value);
}
return details;
The issue is when the text is like this and I only have a key and no value. I cant split the text either by '\r\n' as it doesnt have a value
"Read On"
how can I modify my code to check if this scenario?
Give this a go:
foreach (var row in Rows)
{
var parts = row.Text.Split(new [] { Environment.NewLine }, StringSplitOptions.None);
var key = parts[0];
var value = parts.Length > 1 ? parts[1] : null;
details.Add(key, value);
}
Or even this:
Dictionary<string, string> details =
Rows
.Select(row => row.Text.Split(new [] { Environment.NewLine }, StringSplitOptions.None))
.ToDictionary(parts => parts[0], parts => parts.Length > 1 ? parts[1] : null);

making unique strings after getting value from hashset?

Okay So I have this Hashset that contains 3 items and I want to apply some logic on it such that I am able to append some predefined 3 values for the each item present inside the hashset
for example,
HashSet<string> hs = new HashSet<string>(); //it could be string or some other class object
hs.add("Red");
hs.add("Yellow");
hs.add("Blue");
//Some predefined values for those strings that I want to append to them
string[] str = {Alpha, Beta, Gamma}
The output I desire is:
unique strings associating "RedAlpha", "YellowBeta", "bluegamma"
for example s1 = "RedAlpha", s2 = "YellowBeta", s3 = "bluegamma";
I then want to apply some different logic to each of them later but then I guess that is a different thing
My Tried code
int count = 1;
int index = 0;
string s = "";
foreach(string strr in hs)
{
string s + count = strr + str[index]; // I don't know how to make new unique string
count++;
index++;
}
My other Tried Code,
foreach(string strr in hs)
{
string s = strr + str[index];
s = s + ","
index++;
}
s.split(",");
When you want to merge 2 collection together and perform some operation on them, use the Zip method. See this answer for an explanation of Zip method.
Here is how to achieve what you need:
HashSet<string> hs = new HashSet<string>();
hs.Add("Red");
hs.Add("Yellow");
hs.Add("Blue");
string[] str = { "Alpha", "Beta", "Gamma" };
List<KeyValuePair<string, string>> kvps =
hs.Zip(str, (left, right) => new KeyValuePair<string, string>(left, right))
.ToList();
If you want a dictionary, it is straight forward as well:
Dictionary<string, string> kvps =
hs.Zip(str, (left, right) => new { left, right })
.ToDictionary(x => x.left, x.right);
Put them in a list:
int index = 0;
var list = new List<string>();
foreach(string strr in hs)
{
list.Add(strr + str[index]);
index++;
}
Console.WriteLine(list[0]); //RedAlpha

Getting a count of unique strings from a List<string[]> into a dictionary

I want to input a List<string[]> and
The output is a dictionary where the keys are unique strings used for an index and the values is an array of floats with each position in the array representing the count of the key for a string[] in the List<string[]>
So far here is what I attempted
static class CT
{
//Counts all terms in array
public static Dictionary<string, float[]> Termfreq(List<string[]> text)
{
List<string> unique = new List<string>();
foreach (string[] s in text)
{
List<string> groups = s.Distinct().ToList();
unique.AddRange(groups);
}
string[] index = unique.Distinct().ToArray();
Dictionary<string, float[]> countset = new Dictionary<string, float[]>();
return countset;
}
}
static void Main()
{
/* local variable definition */
List<string[]> doc = new List<string[]>();
string[] a = { "That", "is", "a", "cat" };
string[] b = { "That", "bat", "flew","over","the", "cat" };
doc.Add(a);
doc.Add(b);
// Console.WriteLine(doc);
Dictionary<string, float[]> ret = CT.Termfreq(doc);
foreach (KeyValuePair<string, float[]> kvp in ret)
{
Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
}
Console.ReadLine();
}
I got stuck on the dictionary part. What is the most effective way to implement this?
It sounds like you could use something like:
var dictionary = doc
.SelectMany(array => array)
.Distinct()
.ToDictionary(word => word,
word => doc.Select(array => array.Count(x => x == word))
.ToArray());
In other words, first find the distinct set of words, then for each word, create a mapping.
To create a mapping, look at each array in the original document, and find the count of the occurrences of the word in that array. (So each array maps to an int.) Use LINQ to perform that mapping over the whole document, with ToArray creating an int[] for a particular word... and that's the value for that word's dictionary entry.
Note that this creates a Dictionary<string, int[]> rather than a Dictionary<string, float[]> - it seems more sensible to me, but you could always cast the result of Count to float if you really wanted to.

Dictionary<string, int> increase value

I have a Dictionary<string, int> and I am reading some strings from a list... I want to add them in the dictionary, but if the string is already in the dictionary, I want its value to increase by 1.
The code I tried is as below, but there are some strings that are increased with every input.. Is something wrong?
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach (String recordline in tags)
{
String recordstag = recordline.Split('\t')[1];
String tagToDic = recordstag.Substring(0, (recordstag.Length-1) );
if (dictionary.ContainsKey(tagToDic) == false)
{
dictionary.Add(tagToDic, 1);
}
else
{
try
{
dictionary[tagToDic] = dictionary[tagToDic] + 1;
}
catch (KeyNotFoundException ex)
{
System.Console.WriteLine("X" + tagToDic + "X");
dictionary.Add(tagToDic, 1);
}
}
}
EDIT: To answer your comments... I am removing the last char of the string because it is always a blank space...
My input is like:
10000301 business 0 0,000
10000301 management & auxiliary services 0 0,000
10000316 demographie 0 0,000
10000316 histoire de france 0 0,000
10000347 economics 0 0,000
10000347 philosophy 1 0,500
and i want only the string like "business" or "management & auxiliary services" etc.
You are splitting each string in the input string array and selecting the 2nd string in the string array. Then you are removing the last character of this 2nd string using SubString. Hence all strings that differ only in the last character would be considered the same and incremented. Thats why you might be seeing "some strings that are increased with every input".
EDIT: If the purpose of removing the last char is to remove space, Use String.Trim instead.
Another edit is using TryGetValue instead of ContainsKey which performs better to increment your value. Code has been edited below.
Try this:
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach(string recordline in tags)
{
string recordstag = recordline.Split('\t')[1].Trim();
int value;
if (!dictionary.TryGetValue(recordstag, out value))
dictionary.Add(recordstag, 1);
else
dictionary[recordstag] = value + 1;
}
No need for a dictionary, can be solved using this Linq query.
(Assuming you want the complete string after \t)
var q =
from s in tags.Select (t => t.Substring(t.IndexOf("\t")))
group s by s into g
select new
{
g.Key,
Count = g.Count()
};
And if you need it as a dictionary just add:
var dic = q.ToDictionary (x => x.Key, x => x.Count);
Your input string first split and then substring of it returned to tagToDic, So maybe n strings have a same tagToDic.
Extension method
public static void Increment(this Dictionary<string, int> dictionary, string key)
{
int val;
dictionary.TryGetValue(key, out val);
if (val != null)
dictionary[key] = val + 1;
}
Dictionary<string, int> dictionary = new Dictionary<string, int>();
// fill with some data
dictionary.Increment("someKey");
It's probably easier just to re-add the dictionary value after you retrieve the count from the existing one.
Here's some psuedo code to handle the look up logic.
Dictionary<string, int> _dictionary = new Dictionary<string, int>();
private void AdjustWordCount(string word)
{
int count;
bool success = _dictionary.TryGetValue(word, out count);
if (success)
{
//Remove it
_dictionary.Remove(word);
//Add it back in plus 1
_dictionary.Add(word, count + 1);
}
else //could not get, add it with a count of 1
{
_dictionary.Add(word, 1);
}
}
How about:
Dictionary<string, int> dictionary = new Dictionary<string, int>();
string delimitedTags = "some tab delimited string";
List<string> tags = delimitedTags.Split(new char[] {'\t'}, StringSplitOptions.None).ToList();
foreach (string tag in tags.Distinct())
{
dictionary.Add(tag, tags.Where(t => t == tag).Count());
}
If you have them in a list you could just group them and make your list.
list.GroupBy(recordline => recordline.Split('\t').Substring(0, (recordstag.Length-1),
(key, ienum) => new {word = key, count = ienum.Count()});
Then you can put that in a dictionary or iterate it or something.
Your dictionary code looks like it will function the way you expect.
My best guess is that your string-splitting code is not working correctly.
You'd have to give us some sample inputs to verify this though.
Anyway, your entire block of code could be simplified and rewritten with LINQ as:
var dictionary = tags
.Select(t => {
var recordstag = t.Split('\t')[1];
return recordstag.Substring(0, recordstag.Length-1);
})
.GroupBy(t => t)
.ToDictionary(k => k.Key, v => v.Count())
;

C#: Adding data to dictionary

I have a list like
List<string> TempList = new List<string> { "[66,X,X]", "[67,X,2]", "[x,x,x]" };
I need to add data to the dictionary from the above list
Dictionary<int, int> Dict = new Dictionary<int, int>();
so the Dict should contain
Key --> 66 value --> 67
i need to take 66(first value) from first string([66,X,X]) and 67(first value) from second string( [67,X,X]) and add it as a key value pair into the dictionary.
Now i'm following string replacing and looping methodology to do this .
Is there any way to do this in LINQ or Regular expression.
After your comment that you're starting from a list of lists, I understood what you were after. I'm reusing Jaroslav's 'GetNumber' function here. Wrote my sample with array of array of string, but should work just the same. The code below will throw if you have duplicate keys, which I presume is what you want if you're using a dictionary.
var input = new []
{
new [] { "[66,X,X]", "[67,X,2]", "[x,x,x]" },
new [] { "[5,X,X]", "[8,X,2]", "[x,x,x]" }
};
var query = from l in input
select new
{
Key = GetNumber(l.ElementAt(0)),
Value = GetNumber(l.ElementAt(1))
};
var dictionary = query.ToDictionary(x => x.Key, x => x.Value);
Here is an example using both string.Split() and a Regex:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<string> data = new List<string>() { "[66,X,X]", "[67,X,2]", "[x,x,x]" };
addToDict(data);
Console.ReadKey();
}
private static void addToDict(List<string> items)
{
string key = items[0].Split('[', ',')[1];
string val = items[1].Split('[', ',')[1];
string pattern = #"(?:^\[)(\d+)";
Match m = Regex.Match(items[0], pattern);
key = m.Groups[1].Value;
m = Regex.Match(items[1], pattern);
val = m.Groups[1].Value;
_dict.Add(key, val);
}
static Dictionary<string, string> _dict = new Dictionary<string, string>();
}
}
i suspect that your example is quite contrived though, so there may be a better solution especially if you need to process large numbers of strings into key/value pairs (i deliberately hardcoded index values because your example was quite simple and i didn't want to over complicate the answer). If the input data is consistent in format then you can make assumptions like using fixed indexes, but if there is a possibility of some variance then there may need to be more code to check the validity of it.
You can use a regular expression to extract the value from each item in the list, and if you want, use LINQ to select out two lists and zip them together (in C# 4.0):
var regex = new Regex(#"\d+");
var allValues = TempList.Select(x =>int.Parse(regex.Match(x).Value));
var dictKeys = allValues.Where((x,index)=> index % 2 == 0); //even-numbered
var dictValues = allValues.Where((x,index)=> index % 2 > 0); //odd numbered
var dict = dictKeys.Zip(dictValues, (key,value) => new{key,value})
.ToDictionary(x=>x.key,x=>x.value);
If you're using C# 3.5, you can use Eric Lippert's implementation of Zip().
IF I understand correctly: you want to create linked nodes like 66 -> 67, 67 -> 68, ... n -> n+1?
I would not use LINQ:
private static int GetNumber(string s)
{
int endPos = s.IndexOf(',');
return Int32.Parse(s.Substring(1, endPos-1));
}
And in code:
int first, second;
for (int i = 1; i < TempList.Count; i++)
{
first = GetNumber(TempList[i - 1]);
second = GetNumber(TempList[i]);
Dict.Add(first, second);
}
You should also perform checking, etc.
The sample assumes a list with at least 2 items.
List<List<string>> source = GetSource();
Dictionary<int, int> result = source.ToDictionary(
tempList => GetNumber(tempList[0]),
tempList => GetNumber(tempList[1])
);

Categories

Resources