var fruitDictionary = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) { { "Apple" , "Fruit" }, { "Orange", "Fruit" }, { "Spinach", "Greens" } };
TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd);
string data = textRange.Text;
var output = new StringBuilder(data);
foreach (var kvp in fruitDictionary)
output.Replace(kvp.Key, kvp.Value);
var result = output.ToString();
richTextBox2.AppendText(result);
It works normally but if the input isnt in format it wont work. For example on Apple the output is Fruit but on apple it still says apple
By setting the dictionary's comparer to StringComparer.InvariantCultureIgnoreCase, key lookup became culture and case invariant -- i.e. var a = fruitDictionary["apple"]; and var b = fruitDictionary["ApPlE"] will yield the same results. That said, you perform your replace operation on an instance of StringBuilder which is not related to that. Both StringBuilder.Replace and String.Replace don't have overloads that let you configure string comparison options, so you would have to make an extension method.
public static string Replace(this string str, string oldValue, string newValue,
StringComparison comparison = StringComparison.Ordinal)
{
var index = str.IndexOf(oldValue, comparison);
while (index >= 0)
{
str = str.Remove(index, oldValue.Length);
str = str.Insert(index, newValue);
index = str.IndexOf(oldValue, comparison);
}
return str;
}
var fruitDictionary = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) { { "Apple" , "Fruit" }, { "Orange", "Fruit" }, { "Spinach", "Greens" } };
TextRange textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd);
string data = textRange.Text;
foreach (var kvp in fruitDictionary)
data = data.Replace(kvp.Key, kvp.Value, StringComparison.InvariantCultureIgnoreCase)
richTextBox2.AppendText(data);
Related
I am trying to replace a pattern in my string where only the words between the tags should be replaced. The word that needs to be replaced resides in a dictionary as key and value pair.
Currently this is what I am trying:
string input = "<a>hello</a> <b>hello world</b> <c>I like apple</c>";
string pattern = (#"(?<=>)(.)?[^<>]*(?=</)");
Regex match = new Regex(pattern, RegexOptions.IgnoreCase);
MatchCollection matches = match.Matches(input);
var dictionary1 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
dictionary1.Add("hello", "Hi");
dictionary1.Add("world", "people");
dictionary1.Add("apple", "fruit");
string output = "";
output = match.Replace(input, replace => { return dictionary1.ContainsKey(replace.Value) ? dictionary1[replace.Value] : replace.Value; });
Console.WriteLine(output);
Console.ReadLine();
Using this, it does replace but only the first 'hello' and not the second one. I want to replace every occurrence of 'hello' between the tags.
Any help will be much appreciated.
The problem is that the matches are:
hello
hello world
I like apple
so e.g. hello world is not in your dictionary.
Based on your code, this could be a solution:
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var dictionary1 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
dictionary1.Add("hello", "Hi");
dictionary1.Add("world", "people");
dictionary1.Add("apple", "fruit");
string input = "<a>hello</a> <b>hello world</b> <c>I like apple</c>";
string pattern = ("(?<=>)(.)?[^<>]list|" + GetKeyList(dictionary1) + "(?=</)");
Regex match = new Regex(pattern, RegexOptions.IgnoreCase);
MatchCollection matches = match.Matches(input);
string output = "";
output = match.Replace(input, replace => {
Console.WriteLine(" - " + replace.Value);
return dictionary1.ContainsKey(replace.Value) ? dictionary1[replace.Value] : replace.Value;
});
Console.WriteLine(output);
}
private static string GetKeyList(Dictionary<string, string> list)
{
return string.Join("|", new List<string>(list.Keys).ToArray());
}
}
Fiddle: https://dotnetfiddle.net/zNkEDv
If someone wants to dig into this an tell me why do I need a "list|" in the list (because the first item is being ignored), I'll appreciate it.
This is another way of doing it - I parse the string into XML and then select elements containing the keys in your dictionary and then replace each element's value.
However, you have to have a valid XML document - your example lacks a root node.
var xDocument = XDocument.Parse("<root><a>hello</a> <b>hello world</b> <c>I like apple</c></root>");
var dictionary1 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "hello", "Hi" }, { "world", "people" }, { "apple", "fruit" } };
string pattern = #"\w+";
Regex match = new Regex(pattern, RegexOptions.IgnoreCase);
var xElements = xDocument.Root.Descendants()
.Where(x => dictionary1.Keys.Any(s => x.Value.Contains(s)));
foreach (var xElement in xElements)
{
var updated = match.Replace(xElement.Value,
replace => {
return dictionary1.ContainsKey(replace.Value)
? dictionary1[replace.Value] : replace.Value; });
xElement.Value = updated;
}
string output = xDocument.ToString(SaveOptions.DisableFormatting);
This pattern of "\w+" matches words, not spaces.
This LINQ selects descendants of the root node where the element value contains any of the keys of your dictionary:
var xElements = xDocument.Root.Descendants().Where(x => dictionary1.Keys.Any(s => x.Value.Contains(s)));
I then iterate through the XElement enumerable collection returned and apply your replacement MatchEvaluator to just the string value, which is a lot easier!
The final output is <root><a>Hi</a><b>Hi people</b><c>I like fruit</c></root>. You could then remove the opening and closing <root> and </root> tags, but I don't know what your complete XML looks like.
This will do what you want (from what you have provided so far):
private static Dictionary<string, string> dict;
static void Main(string[] args)
{
dict =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "hello", "Hi" },
{ "world", "people" },
{ "apple", "fruit" }
};
var input = "<a>hello</a> <b>hello world</b> apple <c>I like apple</c> hello";
var pattern = #"<.>([^<>]+)<\/.>";
var output = Regex.Replace(input, pattern, Replacer);
Console.WriteLine(output);
Console.ReadLine();
}
static string Replacer(Match match)
{
var value = match.Value;
foreach (var kvp in dict)
{
if (value.Contains(kvp.Key)) value = value.Replace(kvp.Key, kvp.Value);
}
return value;
}
Given a file with this format
// Colour
$primary-colour: if(#Model.PrimaryColour, #primaryColour, #afd05c);
$secondary-colour: if(#secondaryColour, #secondaryColour, #323f47);
// and so on
I'm trying to replace the #Model.Whatever based on a dictionary with would be something like this
var dictionary = new Dictionary<string, string>
{
{"primaryColour", "blue"},
{"secondaryColour", "red"}
};
But I'm struggling to find a way to so.
I was thinking of doing something like this:
private static String Replace(String str)
{
var dictionary = new Dictionary<string, string>
{
{"primaryColour", "blue"},
{"secondaryColour", "red"}
};
string variableValue;
string pattern = #"#Model.(?<name>\w)";
dictionary.TryGetValue(FirstCharacterToLower("${name}"), out variableValue);
var replacePattern = String.Format("{0}", variableValue);
return Regex.Replace(str, pattern, replacePattern, RegexOptions.IgnoreCase);
}
private static string FirstCharacterToLower(string str)
{
Console.WriteLine(str);
if (String.IsNullOrEmpty(str) || Char.IsLower(str, 0))
return str;
return Char.ToLowerInvariant(str[0]) + str.Substring(1);
}
But what I'm passing to the FirstCharacterToLower is just a string {name} and I'm stuck there. Can't think of a way to do it.
Any idea where to go from here?
Thanks
Edit: Based on sln comment I made this and it works
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var input = #"
// Colour
$primary-colour: if(#Model.PrimaryColour, #Model.PrimaryColour, #afd05c);
$secondary-colour: if(#Model.SecondaryColour, #Model.SecondaryColour, #323f47);";
Console.WriteLine(Replace(input));
}
private static String Replace(String str)
{
var dictionary = new Dictionary<string, string>
{
{"primaryColour", "blue"},
{"secondaryColour", "red"}
};
var regex = new Regex(#"#Model\.(?<name>\w+)");
var output = regex.Replace(str, v =>
{
string outVariable;
dictionary.TryGetValue(GetNameOfVariable(v.Groups["name"].Value), out outVariable);
return outVariable;
});
return output;
}
private static string GetNameOfVariable(string str)
{
Console.WriteLine(str);
if (String.IsNullOrEmpty(str) || Char.IsLower(str, 0))
return str;
return Char.ToLowerInvariant(str[0]) + str.Substring(1);
}
}
As #sln told, you have to use delegate.
private static String Replace(String str)
{
var dictionary = new Dictionary<string, string>
{
{"primaryColour", "blue"},
{"secondaryColour", "red"}
};
string pattern = #"#Model\.(?<name>\w+)";
return Regex.Replace(str, pattern, m =>
{
string key = m.Groups["name"].Value;
key = FirstCharacterToLower(key);
string value = null;
if (dictionary.TryGetValue(key, out value))
return value;
else
return m.Value;
});
}
You'd be better off describing a general regex that matches all your
keys. Then using a delegate replacement.
var dictionary = new Dictionary<string, string>
{
{"primarycolour", "blue"},
{"secondarycolour", "red"}
};
string line_original =
#"
// Colour
$primary-colour: if(#Model.PrimaryColour, #primaryColour, #afd05c);
$secondary-colour: if(#secondaryColour, #secondaryColour, #323f47);
// and so on
";
Regex RxColors = new Regex( #"#Model\.(?<name>\w+)" );
string line_new = RxColors.Replace(
line_original,
delegate(Match match)
{
string outVal;
if ( dictionary.TryGetValue( match.Groups["name"].Value.ToLower(), out outVal) )
return outVal;
return match.Groups[0].Value;
}
);
Console.WriteLine("New line: \r\n\r\n{0}", line_new );
Output:
New line:
// Colour
$primary-colour: if(blue, #primaryColour, #afd05c);
$secondary-colour: if(#secondaryColour, #secondaryColour, #323f47);
// and so on
I'm actually trying to check if a string is equal to any of the key's in my Dictionary object.
Here is what I have done so far:
using (var oStreamReader = new StreamReader(path))
{
Dictionary<String, String> typeNames = new Dictionary<string, string>();
typeNames.Add("Kind","nvarchar(1000)");
typeNames.Add("Name","nvarchar(1000)");
DataTable oDataTable = new DataTable();
var headLine = oStreamReader.ReadLine().Trim().Replace("\"", "");
var columnNames = headLine.Split(new[] { ';' });
String[] oStreamDataValues;
/*
*create DataTable header with specific datatypes and names
*/
int countCol = 0;
foreach (string readColumn in columnNames)
{
if ((readColumn.ToString().Replace("\"", "").CompareTo(typeNames) == true))
{
// this comparison doesn't work
}
}
}
I am not quite sure what exactly you are trying to achieve. If you have a C# dictonary you can use linq to check for values that match the required value, e.g.
string valueToCompare = "Value to match";
Dictionary<string, string> dict = new Dictionary<string, string>
{
{"Key 1", "A value"},
{"Key 2", "Another value"}
};
bool found= dict.Values
.Any(value
=>
value.Equals(valueToCompare,
StringComparison.CurrentCultureIgnoreCase)
);
Since you want check if exist an entry in your Dictionary that as the same key of one of the values in your columnNames object I suggest you to use ContainsKey method
Dictionary<string, string> d = new Dictionary<string, string>();
string keyvalue;
if (d.ContainsKey("value to find"))
{
if (d.TryGetValue("value to find", out keyvalue))
{
//// here keyvalue variable has the value
}
else
{
///value is null or throws exception
}
}
else
{
////key no exists
}
I have solved this (by inspiration of Paul Houlston and Thomas Lielacher)
string headLine = oStreamReader.ReadLine().Trim().Replace("\"", "");
string columnNames = headLine.Split(new[] { ';' });
foreach (string readColumn in columnNames)
{
if (typeNames.Keys.Contains(readColumn, StringComparer.CurrentCultureIgnoreCase) == true)
{
DataColumn oDataColumn = new DataColumn(readColumn,typeof(System.String));
oDataTable.Columns.Add(oDataColumn);
}
}
I need to print a list of countries that use dollars as a currency from a web service.
the data comes form a class called country services which contains this tuple:
public static IEnumerable<Tuple<string, string, string, string>> GetCountryData()
{
var countryService = new CountryServiceProxy.country();
var xmlStringResult = countryService.GetCurrencies();
var result = new List<Tuple<string, string, string, string>>();
var xPathDoc = new XPathDocument(new XmlTextReader(new StringReader(xmlStringResult)));
var navigator = xPathDoc.CreateNavigator();
var nodes = navigator.Select("//Table");
var nodeNames = new[] {"Name", "CountryCode", "Currency", "CurrencyCode"};
while (nodes.MoveNext())
{
var nodeValues = new[] {string.Empty, string.Empty, string.Empty, string.Empty};
for (var i = 0; i < nodeNames.Length; i++)
{
var node = nodes.Current.SelectSingleNode(nodeNames[i]);
if (node != null)
{
nodeValues[i] = node.Value;
}
}
result.Add(new Tuple<string, string, string, string>(nodeValues[0], nodeValues[1], nodeValues[2], nodeValues[3]));
}
return result;
}
I need to call that method and use it to print out a list with countries that use dollars:
private static IEnumerable<Country> Excercise4()
{
// var data = CountryService.GetCountryData().Where(x => x.Item3.Contains("Dollar"));
// ////var data = CountryService.GetCountryData().Where(x => x.Item3 == "Dollar");
// //Console.WriteLine(data);
return new List<Country>
{
new Country("da", "C1", "$", "Dollar"),
new Country("Country2", "C3", "$", "Dollar")
};
}
so far my method looks like this. i cant seem to figure out how to print out the tuple , havent used tuple before.
my write method is as follows:
ConsoleHelper.PrintCountryResults(Excercise4(), "Return a list of Country objects who's currency is Dollar");
ConsoleHelper.Pause(PauseMessage);
and the console helper class looks like this :
public static void PrintCountryResults(IEnumerable<Country> results, string title)
{
PrintHeader(title);
foreach (var result in results)
{
Console.WriteLine("{0, -30}{1, -5}{2, -15}{3, -5}", result.CountryName, result.CountryCode, result.CurrencyName, result.CurrencyCode);
}
}
any help would be appreciated as i said havent used tuple before first try at them ..
Thanx
You can filter the properties of the tuple by referencing the ItemX property with X being the 1-based index of the property that you're interested in. in you case if the currency is the third item, filtering the tuples would look like
var countriesUsingDollarAsACurrency = GetCountryData().Where(tuple => tuple.Item3 == "Dollar");
Is it possible to implicitly declare next Dictionary<HyperLink, Anonymous>:
{ urlA, new { Text = "TextA", Url = "UrlA" } },
{ urlB, new { Text = "TextB", Url = "UrlB" } }
so I could use it this way:
foreach (var k in dic)
{
k.Key.Text = k.Value.Text;
k.Key.NavigateUrl = k.Value.Url;
}
?
How about:
var dict = new[] {
new { Text = "TextA", Url = "UrlA" },
new { Text = "TextB", Url = "UrlB" }
}.ToDictionary(x => x.Url);
// or to add separately:
dict.Add("UrlC", new { Text = "TextC", Url = "UrlC" });
However, you could just foreach on a list/array...
var arr = new[] {
new { Text = "TextA", Url = "UrlA" },
new { Text = "TextB", Url = "UrlB" }
};
foreach (var item in arr) {
Console.WriteLine("{0}: {1}", item.Text, item.Url);
}
You only need a dictionary if you need O(1) lookup via the (unique) key.
Yes, but only with great workaround, and only within a method.
This is how you can do it:
static Dictionary<TKey, TValue> NewDictionary<TKey, TValue>(TKey key, TValue value)
{
return new Dictionary<TKey, TValue>();
}
public void DictRun()
{
var myDict = NewDictionary(new { url="a"},
new { Text = "dollar", Url ="urlA"});
myDict.Add(new { url = "b" }, new { Text = "pound", Url = "urlB" });
myDict.Add(new { url = "c" }, new { Text = "rm", Url = "urlc" });
foreach (var k in myDict)
{
var url= k.Key.url;
var txt= k.Value.Text;
Console.WriteLine(url);
Console.WriteLine(txt);
}
}
You can refer to this SO question for more info.