Format string with dictionary in C# - c#

Suppose I had the following code in Python (yes, this question is about c# this is just an example)
string = "{entry1} {entry2} this is a string"
dictionary = {"entry1": "foo", "entry2": "bar"}
print(string.format(**dictionary))
# output is "foo bar this is just a string"
In this string, it would replace the {entry1} and {entry2} from the string to foo and bar using the .format()
Is there anyway I can replicate this EXACT same thing in C# (and also remove the curly braces) like the following code:
string str1 = "{entry1} {entry2} this a string";
Dictionary<string, string> dict1 = new() {
{"entry1", "foo"},
{"entry2", "bar"}
};
// how would I format this string using the given dict and get the same output?

using string interpolation you could do the following
Dictionary<string, string> dict1 = new() {
{"entry1", "foo"},
{"entry2", "bar"}
};
string result = $"{dict1["entry1"]} {dict1["entry2"]} this is a string";

You can write an extension method for a dictionary and in it manipulate your string as per your need Like
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public static class DictionaryExtensions
{
public static string ReplaceKeyInString(this Dictionary<string, string> dictionary, string inputString)
{
var regex = new Regex("{(.*?)}");
var matches = regex.Matches(inputString);
foreach (Match match in matches)
{
var valueWithoutBrackets = match.Groups[1].Value;
var valueWithBrackets = match.Value;
if(dictionary.ContainsKey(valueWithoutBrackets))
inputString = inputString.Replace(valueWithBrackets, dictionary[valueWithoutBrackets]);
}
return inputString;
}
}
Now use this extension method to convert given string to the expected string,
string input = "{entry1} {entry2} this is a string";
Dictionary<string, string> dictionary = new Dictionary<string, string>
{
{ "entry1", "foo" },
{ "entry2", "bar" }
};
var result = dictionary.ReplaceKeyInString(input);
Console.WriteLine(result);
RegEx logic credit goes to #Fabian Bigler. Here is Fabian's answer:
Get values between curly braces c#
Try online

You can replace values within {...} with a help of regular expressions:
using System.Text.RegularExpressions;
...
string str1 = "{entry1} {entry2} this a string";
Dictionary<string, string> dict1 = new() {
{ "entry1", "foo" },
{ "entry2", "bar" }
};
string result = Regex.Replace(str1, #"{([^}]+)}",
m => dict1.TryGetValue(m.Groups[1].Value, out var v) ? v : "???");
// Let's have a look:
Console.Write(result);
Outcome:
foo bar this a string

try this
foreach (var d in dict) str1=str1.Replace("{"+d.Key+"}",d.Value);
or if you like the extensions
Console.WriteLine(str1.FormatFromDictionary(dict));
public static string FormatFromDictionary(this string str, Dictionary<string, string> dict)
{
foreach (var d in dict) str = str.Replace("{" + d.Key + "}", d.Value);
return str;
}
You can use a string builder if there are may items to replace in one string
public static string FormatFromDictionary(this string str, Dictionary<string, string> dict)
{
StringBuilder sb = new StringBuilder(str, 100);
foreach (var d in dict) sb.Replace("{" + d.Key + "}", d.Value);
return sb.ToString();
}

Related

Inserting data to .CSV file at the same time using foreach

I am new here and actually very new to c#.
In a nutshell, I am using c# via Visual Studio, I am calling a data from a database and I want to save these data in a .csv file. The problem now is that I want to save these data on two columns at the same time.
My code do write them in a file but shifted not on the right rows.
Dictionary<string, string> elementNames = new Dictionary<string, string>();
Dictionary<string, string> elementTypes = new Dictionary<string, string>();
var nodes = webservice.nepService.GetAllElementsOfElementType(webservice.ext, "Busbar", ref elementNames, ref elementTypes);
Dictionary<string, string> nodeResults = new Dictionary<string, string>();
Dictionary<string, string> nodeResults1 = new Dictionary<string, string>();
foreach (var nodename in elementNames.Values)
{
var nodeRes = webservice.nepService.GetResultElementByName(webservice.ext, nodename, "Busbar", -1, "LoadFlow", null);
var Uvolt = GetXMLAttribute(nodeRes, "U");
nodeResults.Add(nodename, Uvolt);
var Upercentage = GetXMLAttribute(nodeRes, "Up");
nodeResults1.Add(nodename, Upercentage);
StringBuilder strBldr = new StringBuilder();
string outputFile = #"C:\Users\12.csv";
string separator = ",";
foreach (var res in nodeResults)
{
strBldr.AppendLine($"{res.Key}{separator}{res.Value}");
}
foreach (var res1 in nodeResults1)
{
strBldr.AppendLine($"{separator}{separator}{res1.Value}");
}
File.WriteAllText(outputFile, strBldr.ToString());
}
this is the output of the previous code:
https://ibb.co/T4trQC3
I want these shifted values to move up beside the other values like that:
https://ibb.co/4S25v0h
Thank you
if you look to the code you are using AppendLine
strBldr.AppendLine($"{separator}{separator}{res1.Value}");
and if you want to append on same line just use Append
strBldr.Append($"{separator}{separator}{res1.Value}");
EDITED:
in linq you can use Zip function to zip to lists
// using System.Linq;
var results = Results.Zip(Results1, (firstList, secondList) => firstList.Key + "," + firstList.Value + "," + secondList.Value);
Edit Full example
public static IDictionary<string, string> Results { get; set; }
public static IDictionary<string, string> Results1 { get; set; }
private static void Main(string[] args)
{
StringBuilder strBldr = new StringBuilder();
string outputFile = #"D:\12.csv";
Results = new Dictionary<string, string>()
{
{"N1", "20"},
{"N2", "0.399992"},
{"N3", "0.369442"},
{"N4", "0.369976"}
};
Results1 = new Dictionary<string, string>()
{
{"N1", "100"},
{"N2", "99.9805"},
{"N3", "92.36053"},
{"N4", "92.49407"}
};
IEnumerable<string> results = Results.Zip(Results1,
(firstList, secondList) => firstList.Key + "," + firstList.Value + "," + secondList.Value);
foreach (string res1 in results)
{
strBldr.AppendLine(res1);
}
File.WriteAllText(outputFile, strBldr.ToString());
}
for faster code you can try this
HashSet<Tuple<string, string, string>> values = new HashSet<Tuple<string, string, string>>();
var nodes = webservice.nepService.GetAllElementsOfElementType(webservice.ext, "Busbar", ref elementNames, ref elementTypes);
foreach (var nodename in elementNames.Values)
{
var nodeRes = webservice.nepService.GetResultElementByName(webservice.ext, nodename, "Busbar", -1, "LoadFlow", null);
var Uvolt = GetXMLAttribute(nodeRes, "U");
var Upercentage = GetXMLAttribute(nodeRes, "Up");
values.Add(Tuple.Create(nodename, Uvolt, Upercentage));
}
var output = string.Join("\n", values.ToList().Select(tuple => $"{tuple.Item1},{tuple.Item2},{tuple.Item3}").ToList());
string outputFile = #"C:\Users\12.csv";
File.WriteAllText(outputFile, output);
if the rowCount for Results and Results1 are same and the keys are in the same order, try:
for (int i = 0; i < Results.Count; i++)
strBldr.AppendLine($"{Results[i].Key}{separator}{Results[i].Value}{separator}{Results1[i].Value}");
Or, if the rows are not in the same order, try:
foreach (var res in Results)
strBldr.AppendLine($"{res.Key}{separator}{res.Value}{separator}{Results1.Single(x => x.Key == res.Key).Value}");

Parse String with Regex c#

I am trying to customise a DevExpress grid filter.
Currently I return the data from my api, and so I need to parse the built in string which is returned from the grid.
An example of the filter string is;
StartsWith([name], 'test') And StartsWith([quantity], '12') And
StartsWith([id], '1') And StartsWith([date], '01/10/2015')
I would like to convert this to a Dictionary in the most efficient way?
You could use Regex for filtering the key/value pair outta your string and a simple foreach to prepare the dictionary.
This could be a solution:
public static Dictionary<string, object> FilterAPIData(string data)
{
var r = new Regex(#"\[\w+\], \'[\w/]+\'");
var result = r.Matches(data);
var dict = new Dictionary<string, object>();
foreach (Match item in result)
{
var val = item.Value.Split(',');
dict.Add(val[0], val[1]);
}
return dict;
}
Regex might be the best option for this, but I'll show you how to do it without Regex as it can be a bit difficult to understand.
Assuming your string will always be in this format you can do this:
string str = "StartsWith([name], 'test') And StartsWith([quantity], '12') And StartsWith([id], '1') And StartsWith([date], '01/10/2015')";
var strArray = str.Split(new string[]{"And "}, StringSplitOptions.None);
var dict = new Dictionary<string, string>();
foreach(var value in strArray)
{
dict.Add(GetStringBetween(value, "[", "]"), GetStringBetween(value, "'", "'"));
}
private string GetStringBetween(string value, string startDelim, string endDelim)
{
int first = value.IndexOf(startDelim) + startDelim.Length;
int last = value.LastIndexOf(endDelim);
return value.Substring(first, last - first);
}
//Output :
//name test
//quantity 12
//id 1
// date 01/10/2015
If there other formats the string can be in you can adjust as needed. I would also consider adding in more validation/error handling, but I will let you figure that out ;)

C# Replacing String in Foreach loop

I'm trying to replace text. I'm using a dictionary for the task.
public static string cleanString(this String str) {
Dictionary<string, string> dict = new Dictionary<string,string>();
dict.Add("JR", "Junior");
dict.Add("SR", "Senior");
foreach (KeyValuePair<string,string> d in dict) {
if (str.BlindContains(p.Key)) {
str = str.BlindReplace(str, p.Value);
}
}
return str;
}
BlindContains and BlindReplace just ignore the case of the replacement (and BC ensures the string is not part of another word):
public static bool BlindContains(this String str, string toCheck)
{
if (Regex.IsMatch(str, #"\b" + toCheck + #"\b", RegexOptions.IgnoreCase))
return str.IndexOf(toCheck, StringComparison.OrdinalIgnoreCase) >= 0;
return false;
}
public static string BlindReplace(this String str, string oldStr, string newStr)
{
return Regex.Replace(str, oldStr, newStr, RegexOptions.IgnoreCase);
}
The problem
If I call my method on a a string, the following occurs:
string mystring = "The jr. is less than the sr."
mystring.cleanString()
returns "Junior"
However, when I print
Console.WriteLine(Regex.Replace(mystring, "jr.", "junior", Regex.IgnoreCase));
I get the output: "The junior is less than the sr."
Why does the loop compromise the task?
You should be passing the key in your dictionary (Which contains the text to search for), rather than the actual string you are searching in.
It should be:
str = str.BlindReplace(p.Key, p.Value);
As opposed to:
str = str.BlindReplace(str, p.Value);
You are currently replacing your string with the value "Junior" because you specified your string as the text to search for. (Which will make it replace the entire string instead of just the keyword)
In cleanString implementation, I think you made an error in your call to BlindReplace. Instead of:
str = str.BlindReplace(str, p.Value);
I believe you should have called:
str = str.BlindReplace(d.Key, d.Value);

Parse through POST

I use Stream reader to read context.Request.InputStream to the end and end up with a string looking like
"Gamestart=true&GamePlayer=8&CurrentDay=Monday&..."
What would be the most efficent/"clean" way to parse that in a C# console?
You can use HttpUtility.ParseQueryString
Little sample:
string queryString = "Gamestart=true&GamePlayer=8&CurrentDay=Monday"; //Hardcoded just for example
NameValueCollection qscoll = HttpUtility.ParseQueryString(querystring);
foreach (String k in qscoll.AllKeys)
{
//Prints result in output window.
System.Diagnostics.Debug.WriteLine(k + " = " + qscoll[k]);
}
HttpUtility.ParseQueryString
Parses a query string into a NameValueCollection using UTF8 encoding.
http://msdn.microsoft.com/en-us/library/ms150046.aspx
I know this is a bit of a zombie post but I thought I'd add another answer since HttpUtility adds another assembly reference (System.Web), which may be undesirable to some.
using System.Net;
using System.Text.RegularExpressions;
static readonly Regex HttpQueryDelimiterRegex = new Regex(#"\?", RegexOptions.Compiled);
static readonly Regex HttpQueryParameterDelimiterRegex = new Regex(#"&", RegexOptions.Compiled);
static readonly Regex HttpQueryParameterRegex = new Regex(#"^(?<ParameterName>\S+)=(?<ParameterValue>\S*)$", RegexOptions.Compiled);
static string GetPath(string pathAndQuery)
{
var components = HttpQueryDelimiterRegex.Split(pathAndQuery, 2);
return components[0];
}
static Dictionary<string, string> GetQueryParameters(string pathAndQuery)
{
var parameters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var components = HttpQueryDelimiterRegex.Split(pathAndQuery, 2);
if (components.Length > 1)
{
var queryParameters = HttpQueryParameterDelimiterRegex.Split(components[1]);
foreach(var queryParameter in queryParameters)
{
var match = HttpQueryParameterRegex.Match(queryParameter);
if (!match.Success) continue;
var parameterName = WebUtility.HtmlDecode(match.Groups["ParameterName"].Value) ?? string.Empty;
var parameterValue = WebUtility.HtmlDecode(match.Groups["ParameterValue"].Value) ?? string.Empty;
parameters[parameterName] = parameterValue;
}
}
return parameters;
}
I wish they would add that same method to WebUtility which is available in System.Net as of .NET 4.0.

foreach KeyValuePair in NameValueCollection?

I have this code:
NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (KeyValuePair<String,String> pr in nv) {
//process KeyValuePair
}
This compiles, but when I try to run it I get an InvalidCastException.
Why is this? Why can't I use KeyValuePair to iterate over a NameValueCollection, and what should I use instead?
First of all, NameValueCollection doesn't use KeyValuePair<String,String>. Also, foreach only exposes the key:
NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (string key in nv) {
var value = nv[key];
}
You can't do that directly, but you can create an extension method like so:
public static IEnumerable<KeyValuePair<string, string>> AsKVP(
this NameValueCollection source
)
{
return source.AllKeys.SelectMany(
source.GetValues,
(k, v) => new KeyValuePair<string, string>(k, v));
}
Then you can do:
NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (KeyValuePair<String,String> pr in nv.AsKVP()) {
//process KeyValuePair
}
Note: inspired by this. SelectMany is required to handle duplicate keys.
vb.net version:
<Extension>
Public Function AsKVP(
source As Specialized.NameValueCollection
) As IEnumerable(Of KeyValuePair(Of String, String))
Dim result = source.AllKeys.SelectMany(
AddressOf source.GetValues,
Function(k, v) New KeyValuePair(Of String, String)(k, v))
Return result
End Function
For future reference, you could also use this syntax:
foreach(string key in Request.QueryString)
{
var value = Request.QueryString[key];
}
ANother extension method, for learning purposes:
public static IEnumerable<KeyValuePair<string, string>> ToIEnumerable(this NameValueCollection nvc)
{
foreach (string key in nvc.AllKeys)
{
yield return new KeyValuePair<string, string>(key, nvc[key]);
}
}
NameValueCollection uses the old-skool enumerator:
var enu = ConfigurationManager.AppSettings.GetEnumerator();
while(enu.MoveNext())
{
string key = (string)enu.Current;
string value = ConfigurationManager.AppSettings[key];
}
I did like this and it works:
foreach (string akey in request.Query.Keys.Cast<string>())
writer.WriteLine(akey + " = " + request.Query[akey]);
Be aware that the key name might appear more than once in the query string and that the comparison is usually case sensitive.
If you want to just get the value of the first matching key and not bothered about case then use this:
public string GetQueryValue(string queryKey)
{
foreach (string key in QueryItems)
{
if(queryKey.Equals(key, StringComparison.OrdinalIgnoreCase))
return QueryItems.GetValues(key).First(); // There might be multiple keys of the same name, but just return the first match
}
return null;
}
To convert nameValueCollection from QueryString to List you can use extension like this:
public static List<KeyValuePair<string, string>> GetParams(this HttpRequest request)
{
var nameValueCollection = HttpUtility.ParseQueryString(request.QueryString.Value);
List<KeyValuePair<string, string>> keyValueCollections = new List<KeyValuePair<string, string>>();
if (!nameValueCollection.IsNullOrEmpty())
{
foreach (var key in nameValueCollection.AllKeys)
{
keyValueCollections.Add(new KeyValuePair<string, string>(key, nameValueCollection[key]));
}
}
return keyValueCollections;
}
public static void PrintKeysAndValues2( NameValueCollection myCol )
{
Console.WriteLine( " [INDEX] KEY VALUE" );
for ( int i = 0; i < myCol.Count; i++ )
Console.WriteLine( " [{0}] {1,-10} {2}", i, myCol.GetKey(i), myCol.Get(i) );
Console.WriteLine();
}
http://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection.aspx

Categories