Dictionary ContainsKey() Doesn't compare string values - c#

I'm a noobie to C# and don't understand why ContainsKey() won't return true with the values bid_amount and bid_amount.
I have a long string ("key=value, key=value...") that is split on , and then = created into a dictionary. But when I test for a known key in the dict bid_amount it always returns false.
Is there a way to compare values using ContainsKey()?
What exactly am I missing?
string[] responseStringSplit = responseString.Split (new []{ ',' });
Dictionary<string,string> responseDict = new Dictionary<string, string> ();
foreach (string word in responseStringSplit)
{
string[] splitString = word.Split (new[]{ ':' });
if (splitString.Length > 1)
{
responseDict.Add(splitString [0],splitString [1]);
}
else
{
Debug.Log ("CouldNotSplit:" + word);
}
}
foreach (KeyValuePair<string, string> entry in responseDict)
{
Debug.Log (entry.Key + "=" + entry.Value);
if (responseDict.ContainsKey ("bid_amount"))
{
Debug.Log ("found bid amount");
}
}

Based on your input, you've got a space between your , and your key. Try calling .Trim() on splitString[0] and splitString[1] before you add it to your dictionary.
responseDict.Add(splitString[0].Trim(), splitString[1].Trim());

If you look carefully at the code and input you have 2 problems:
You first split by "," whereas your input code is "someText, someTextAfterSpace" - So split by ", ".
Then you split by ":" whereas your input code is with "="
Code should be:
//split by ", " (plus the space) to avoid that problem (or use trim)
string[] responseStringSplit = responseString.Split (new []{ ", " });
//split by "=" instead of current ":" (Or change input string from having '=' to ':')
string[] splitString = word.Split (new[]{ '=' });
Besides changing that, adding Trim()s and ToLower() for the key is of course welcome in order to avoid problems with white spaces and uppercase/lowercase

Related

Compare two strings to find any duplicates

I really cannot find the answer to this question and it is driving me mental. I have two strings, one is a text file that is read into a string called logfile. The other is just a user input string, called text1. Eventually it's just going to be a guessing game with hints, but I can't figure out how to compare these two for equality.
string LOG_PATH = "E:\\Users\\start.txt";
string logfile = File.ReadAllText(LOG_PATH);
string text1 = "";
text1 = Console.ReadLine();
if (logfile.Contains(text1))
{
Console.WriteLine("found");
}
else
{
Console.WriteLine("not found");
}
This code works fine when there is only one word in the text file and matches. If the text file only contains the word "Mostly" and the user entered mostly and a bunch of other words, the console prints found. But if the text file has mostly and a bunch of other random words, say "Mostly cloudy today", the console prints not found. Is it possible to match to strings for ANY duplicates at all?
You can try it with different ways,
Using Except(),
var wordsFromFile = File.ReadAllText(LOG_PATH).Split(' ').ToList();
var inputWords = Console.ReadLine().Split(' ').ToList();
Console.WriteLine(wordsFromFile.Except(inputWords).Any() ? "Found" : "Not Found");
Similar way using foreach() loop,
var wordsFromFile = File.ReadAllText(LOG_PATH).Split(' ').ToList();
var inputWords = Console.ReadLine();
string result = "Not Found";
foreach(var word in inputWords)
{
if(wordsFromFile.Contains(word))
{
result = "Found";
break
}
}
Console.WriteLine(result);
Very similar to what Prasad did, except we ignore blank lines and use a case-insensitive comparison:
string LOG_PATH = #"E:\Users\start.txt";
List<String> logfileWords = new List<String>();
foreach (String line in File.ReadLines(LOG_PATH))
{
logfileWords.AddRange(line.Trim().Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries));
}
Console.Write("Words to search for (separated by spaces): ");
String[] inputs = Console.ReadLine().Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine("Inputs:");
foreach(String input in inputs)
{
Console.WriteLine(input + " ==> " + (logfileWords.Any(w => w.Equals(input, StringComparison.InvariantCultureIgnoreCase)) ? "found" : "not found"));
}

Parsing a text file with a custom format in C#

I have a bunch of text files that has a custom format, looking like this:
App Name
Export Layout
Produced at 24/07/2011 09:53:21
Field Name Length
NAME 100
FULLNAME1 150
ADDR1 80
ADDR2 80
Any whitespaces may be tabs or spaces. The file may contain any number of field names and lengths.
I want to get all the field names and their corresponding field lengths and perhaps store them in a dictionary. This information will be used to process a corresponding fixed width data file having the mentioned field names and field lengths.
I know how to skip lines using ReadLine(). What I don't know is how to say: "When you reach the line that starts with 'Field Name', skip one more line, then starting from the next line, grab all the words on the left column and the numbers on the right column."
I have tried String.Trim() but that doesn't remove the whitespaces in between.
Thanks in advance.
You can use SkipWhile(l => !l.TrimStart().StartsWith("Field Name")).Skip(1):
Dictionary<string, string> allFieldLengths = File.ReadLines("path")
.SkipWhile(l => !l.TrimStart().StartsWith("Field Name")) // skips lines that don't start with "Field Name"
.Skip(1) // go to next line
.SkipWhile(l => string.IsNullOrWhiteSpace(l)) // skip following empty line(s)
.Select(l =>
{ // anonymous method to use "real code"
var line = l.Trim(); // remove spaces or tabs from start and end of line
string[] token = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
return new { line, token }; // return anonymous type from
})
.Where(x => x.token.Length == 2) // ignore all lines with more than two fields (invalid data)
.Select(x => new { FieldName = x.token[0], Length = x.token[1] })
.GroupBy(x => x.FieldName) // groups lines by FieldName, every group contains it's Key + all anonymous types which belong to this group
.ToDictionary(xg => xg.Key, xg => string.Join(",", xg.Select(x => x.Length)));
line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries) will split by space and tabs and ignores all empty spaces. Use GroupBy to ensure that all keys are unique in the dictionary. In the case of duplicate field-names the Length will be joined with comma.
Edit: since you have requested a non-LINQ version, here is it:
Dictionary<string, string> allFieldLengths = new Dictionary<string, string>();
bool headerFound = false;
bool dataFound = false;
foreach (string l in File.ReadLines("path"))
{
string line = l.Trim();
if (!headerFound && line.StartsWith("Field Name"))
{
headerFound = true;
// skip this line:
continue;
}
if (!headerFound)
continue;
if (!dataFound && line.Length > 0)
dataFound = true;
if (!dataFound)
continue;
string[] token = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (token.Length != 2)
continue;
string fieldName = token[0];
string length = token[1];
string lengthInDict;
if (allFieldLengths.TryGetValue(fieldName, out lengthInDict))
// append this length
allFieldLengths[fieldName] = lengthInDict + "," + length;
else
allFieldLengths.Add(fieldName, length);
}
I like the LINQ version more because it's much more readable and maintainable (imo).
Based on the assumption that the position of the header line is fixed, we may consider actual key-value pairs to start from the 9th line. Then, using the ReadAllLines method to return a String array from the file, we just start processing from index 8 onwards:
string[] lines = File.ReadAllLines(filepath);
Dictionary<string,int> pairs = new Dictionary<string,int>();
for(int i=8;i<lines.Length;i++)
{
string[] pair = Regex.Replace(lines[i],"(\\s)+",";").Split(';');
pairs.Add(pair[0],int.Parse(pair[1]));
}
This is a skeleton, not accounting for exception handling, but I guess it should get you started.
You can use String.StartsWith() to detect "FieldName". Then String.Split() with a parameter of null to split by whitespace. This will get you your fieldname and length strings.

Formatting List into string

So im running a little issue with my code, how can I convert my List into a normal string with this format
listitem1, listitem2, listitem3
Note that last listitem3 does not have a , at the end.
I tried String.Inser but I cant get the last index..
Sound like you need string.Join
var result = string.Join(", ", yourList);
Use a join which will concatenate your array with a delimiter which you define.
Likewise use a split to turn your delimited spring into an array.
var list = new List<string> {"one", "two", "three"};
var stringList = string.Join(", ", list);
the result will be
one, two, three
If you want the space make sure to include it in the delimeter ", "
Or you can use a foreach loop, to get last item then based on that append a comma :
List<string> mylist = new List<string>();
mylist.Add("Test1");
mylist.Add("Test2");
mylist.Add("Test3");
string lastItem = mylist[mylist.Count - 1];
foreach(string s in mylist)
{
if (s != lastItem)
Console.Write(s + ", ");
else
Console.Write(s);
}
and the result is:
Test1, Test2, Test3

Last item in the c# list

I am need to highlight the keywords or tags in an article & pass variable to jQuery Array
i am using property to pass value from C# to java-script which works i also need to format my keywords which in database are stored like one, two, three, four, five,six,seven
in order to make it work i have to wrap each keyword in single ' or double quotes".
JQuery
function HighlightKeywords(keywords) {
var el = $("body");
$(keywords).each(function () {
var pattern = new RegExp("(" + this + ")", ["gi"]);
var rs = "<a href='search.aspx?search=$1'<span style='background-color:#FFFF00;font-weight: bold;background-color:#FFFF00;'>$1</span></a>";
el.html(el.html().replace(pattern, rs));
});
}
HighlightKeywords(["<%= MyProperty %>]");
C# Code
string _sFinalList = null;
protected string MyProperty { get { return _sFinalList; } }
string sKewords = "one, two, three, four, five,six,seven";
List<string> lstKewords = sKewords.Split(',').ToList();
foreach (string list in lstKewords) // Loop through List with foreach
{
_sFinalList += "'" + list + "',";
}
Problem with this code is that it ads , after the last words i want to know what is the best way to avoid adding , after the last words
Current OUTPUT: "'one', 'two', 'three', 'four', 'five','six','seven',"
Desired OUTPUT: "'one', 'two', 'three', 'four', 'five','six','seven'"
Help in this regard is appreciated
In C#, use String.Join() :
List<string> lstKeywords = sKeywords.Split(',').ToList();
var quotedKeywords = lstKeywords.Select(s => "'" + s + "'");
string _sFinalList = string.Join(",", quotedKeywords);
You can using String.Join()
string sKewords = "one, two, three, four, five,six,seven";
List<string> lstKewords = sKewords.Split(',').ToList();
var _partial = lstKewords.Select(x => "'" + x + "'");
Var _result = String.Join(",", _partial);
String.Join()
You could use String.Join
var result = string.Format("'{0}'", string.Join("','", yourList));
you may simply remove lastIndexOf(",") with substring function of string class after you complete the for loop.
OR
you may add and if statement in your for loop that would add "," if loop is not in its last iteration.

What is the best way to parse this string in C#?

I have a string that I am reading from another system. It's basically a long string that represents a list of key value pairs that are separated by a space in between. It looks like this:
key:value[space]key:value[space]key:value[space]
So I wrote this code to parse it:
string myString = ReadinString();
string[] tokens = myString.split(' ');
foreach (string token in tokens) {
string key = token.split(':')[0];
string value = token.split(':')[1];
. . . .
}
The issue now is that some of the values have spaces in them so my "simplistic" split at the top no longer works. I wanted to see how I could still parse out the list of key value pairs (given space as a separator character) now that I know there also could be spaces in the value field as split doesn't seem like it's going to be able to work anymore.
NOTE: I now confirmed that KEYs will NOT have spaces in them so I only have to worry about the values. Apologies for the confusion.
Use this regular expression:
\w+:[\w\s]+(?![\w+:])
I tested it on
test:testvalue test2:test value test3:testvalue3
It returns three matches:
test:testvalue
test2:test value
test3:testvalue3
You can change \w to any character set that can occur in your input.
Code for testing this:
var regex = new Regex(#"\w+:[\w\s]+(?![\w+:])");
var test = "test:testvalue test2:test value test3:testvalue3";
foreach (Match match in regex.Matches(test))
{
var key = match.Value.Split(':')[0];
var value = match.Value.Split(':')[1];
Console.WriteLine("{0}:{1}", key, value);
}
Console.ReadLine();
As Wonko the Sane pointed out, this regular expression will fail on values with :. If you predict such situation, use \w+:[\w: ]+?(?![\w+:]) as the regular expression. This will still fail when a colon in value is preceded by space though... I'll think about solution to this.
This cannot work without changing your split from a space to something else such as a "|".
Consider this:
Alfred Bester:Alfred Bester Alfred:Alfred Bester
Is this Key "Alfred Bester" & value Alfred" or Key "Alfred" & value "Bester Alfred"?
string input = "foo:Foobarius Maximus Tiberius Kirk bar:Barforama zap:Zip Brannigan";
foreach (Match match in Regex.Matches(input, #"(\w+):([^:]+)(?![\w+:])"))
{
Console.WriteLine("{0} = {1}",
match.Groups[1].Value,
match.Groups[2].Value
);
}
Gives you:
foo = Foobarius Maximus Tiberius Kirk
bar = Barforama
zap = Zip Brannigan
You could try to Url encode the content between the space (The keys and the values not the : symbol) but this would require that you have control over the Input Method.
Or you could simply use another format (Like XML or JSON), but again you will need control over the Input Format.
If you can't control the input format you could always use a Regular expression and that searches for single spaces where a word plus : follows.
Update (Thanks Jon Grant)
It appears that you can have spaces in the key and the value. If this is the case you will need to seriously rethink your strategy as even Regex won't help.
string input = "key1:value key2:value key3:value";
Dictionary<string, string> dic = input.Split(' ').Select(x => x.Split(':')).ToDictionary(x => x[0], x => x[1]);
The first will produce an array:
"key:value", "key:value"
Then an array of arrays:
{ "key", "value" }, { "key", "value" }
And then a dictionary:
"key" => "value", "key" => "value"
Note, that Dictionary<K,V> doesn't allow duplicated keys, it will raise an exception in such a case. If such a scenario is possible, use ToLookup().
Using a regular expression can solve your problem:
private void DoSplit(string str)
{
str += str.Trim() + " ";
string patterns = #"\w+:([\w+\s*])+[^!\w+:]";
var r = new System.Text.RegularExpressions.Regex(patterns);
var ms = r.Matches(str);
foreach (System.Text.RegularExpressions.Match item in ms)
{
string[] s = item.Value.Split(new char[] { ':' });
//Do something
}
}
This code will do it (given the rules below). It parses the keys and values and returns them in a Dictonary<string, string> data structure. I have added some code at the end that assumes given your example that the last value of the entire string/stream will be appended with a [space]:
private Dictionary<string, string> ParseKeyValues(string input)
{
Dictionary<string, string> items = new Dictionary<string, string>();
string[] parts = input.Split(':');
string key = parts[0];
string value;
int currentIndex = 1;
while (currentIndex < parts.Length-1)
{
int indexOfLastSpace=parts[currentIndex].LastIndexOf(' ');
value = parts[currentIndex].Substring(0, indexOfLastSpace);
items.Add(key, value);
key = parts[currentIndex].Substring(indexOfLastSpace + 1);
currentIndex++;
}
value = parts[parts.Length - 1].Substring(0,parts[parts.Length - 1].Length-1);
items.Add(key, parts[parts.Length-1]);
return items;
}
Note: this algorithm assumes the following rules:
No spaces in the values
No colons in the keys
No colons in the values
Without any Regex nor string concat, and as an enumerable (it supposes keys don't have spaces, but values can):
public static IEnumerable<KeyValuePair<string, string>> Split(string text)
{
if (text == null)
yield break;
int keyStart = 0;
int keyEnd = -1;
int lastSpace = -1;
for(int i = 0; i < text.Length; i++)
{
if (text[i] == ' ')
{
lastSpace = i;
continue;
}
if (text[i] == ':')
{
if (lastSpace >= 0)
{
yield return new KeyValuePair<string, string>(text.Substring(keyStart, keyEnd - keyStart), text.Substring(keyEnd + 1, lastSpace - keyEnd - 1));
keyStart = lastSpace + 1;
}
keyEnd = i;
continue;
}
}
if (keyEnd >= 0)
yield return new KeyValuePair<string, string>(text.Substring(keyStart, keyEnd - keyStart), text.Substring(keyEnd + 1));
}
I guess you could take your method and expand upon it slightly to deal with this stuff...
Kind of pseudocode:
List<string> parsedTokens = new List<String>();
string[] tokens = myString.split(' ');
for(int i = 0; i < tokens.Length; i++)
{
// We need to deal with the special case of the last item,
// or if the following item does not contain a colon.
if(i == tokens.Length - 1 || tokens[i+1].IndexOf(':' > -1)
{
parsedTokens.Add(tokens[i]);
}
else
{
// This bit needs to be refined to deal with values with multiple spaces...
parsedTokens.Add(tokens[i] + " " + tokens[i+1]);
}
}
Another approach would be to split on the colon... That way, your first array item would be the name of the first key, second item would be the value of the first key and then name of the second key (can use LastIndexOf to split it out), and so on. This would obviously get very messy if the values can include colons, or the keys can contain spaces, but in that case you'd be pretty much out of luck...

Categories