I have a list of String defined like this
List<String> lstHeaders = new List<string>(new String[]{"str","str1","str2"});
I a want a way to join all that elemenet with an specific format to get something like this
<td>str</td><td>str1</td><td>str2</td>
but get this just in online of code , something like this
"<th>"+lstHeaders.functionToGetFormated()+"</th>"
Use the StringBuilder class:
var sb = new StringBuilder();
lstHeaders.ForEach(x => sb.AppendFormat("<td>{0}</td>", x));
return sb.toString()
Probably makes more sense to write your own extension method for this. Nothing built-in will work exactly like that (that I am aware of).
public static string WrapElementsWithTag(this IEnumerable<string> input, string wrapper)
{
StringBuilder sb = new StringBuilder();
foreach (string s in input)
{
sb.AppendFormat("<{1}>{0}</{1}>", s, wrapper);
}
return sb.ToString();
}
Called like;
lstHeaders.WrapElementsWithTag("td");
Using Linq and String.Concat:
string result = string.Concat(lstHeaders.Select(s => string.Format("<td>{0}</td>", s)));
Perhaps clearer with query syntax:
string result = string.Concat(from s in lstHeaders select string.Format("<td>{0}</td>", s));
You could do something like:
string.Format("<td>{0}</td>", string.Join("</td><td>", lstHeaders));
Generally simple string concatenation is risky against constructing meta data like html and xml. If you are absolute sure that the text wont corrupt the meta syntax, you may surely do it with with StringBuilder or LinQ. Say, those strings were already escaped.
Otherwise, you may consider using HtmlTextWriter. And it shouldn't be hard to use HtmlTextWriter in LINQ.
Related
I want to send a HTTP request that looks like this:
http://api.com/main?id=1234&id=5678, the id will be GUID in string eventually.
I tried the below piece of code:
var idString = string.Join(",", listOfIds);
var queryString = new Dictionary<string, string>
{
{"id", idString}
};
requestUri = QueryHelpers.AddQueryString(requestUri, queryString);
This will give me like: http://api.com/main?id=1234,5678 but I want the style like above.
Is there anyway to achieve this without using for loop?
Thanks!
QueryHelpers doesn't work with arrays because there's no standard way to pass an array of values in a query string. Some applications accept id=1,2,3 others id=1&id=2&id=3 while others id[0]=1&id[1]=2&id[2]=3.
.NET (Core) 5 and later
AddQueryString now works with lists of KeyValuePair<string,string>or KeyValuePair<string,StringValues>
var parameters=new []{
new KeyValuePair("id",new StringValues(arrayOfIds)),
new KeyValuePair("other","value"),
...
};
var finalUri=QueryHelpers.AddQueryString(requestUri, parameters);
The StringValues constructors accept either a single string or an array of strings
Before .NET (Core) 5
String.Join itself uses a loop and a StringBuilder to create a new string without allocating temporary strings. Strings are immutable, so any string modification operation results in a new temporary string.
You could use the source code as a guide to build your own loop. A quick solution could be something like this:
string ArrayToQueryString_DONT_USE(string name,string[] values)
{
var result=new StringBuilder();
result.AppendFormat("{0}={1}",name,value);
for(int i=1;i<values.Length;i++)
{
result.AppendFormat("&{0}={1}',name,values[i]);
}
return result.ToString();
}
Unfortunately, that won't work if the parameter names or values need encoding. That's what AddQueryString does, using, once again, a StringBuilder to avoid allocating temporary strings. We can borrow that code as well:
string ArrayToQueryString(string name,string[] values)
{
var result=new StringBuilder();
result.AppendFormat("{0}={1}",name,value);
for(int i=1;i<values.Length;i++)
{
result.Append('&');
result.Append(UrlEncoder.Default.Encode(name));
result.Append('=');
result.Append(UrlEncoder.Default.Encode(values[i]));
}
return result.ToString();
}
This is something I have always wondered about, and looked up a few times but have never figured out.
So basically what I want to do is get something to this effect:
List<string> strings = new List<string>(){"a","b","c"};
string aString = foreach(string s in strings){ if (s == "c") return s;}
so then after that, aString has the value "c".
I have tried using lambda expressions, maybe I just cant get them to work right, or maybe there is just no way to do this.
And obviously I want to do something a bit more complicated than in my example above, but it will work the same way.
Possible? not possible?
You should use the FirstOrDefault Extension method.
List<string> strings = new List<string>(){"a","b","c"};
return strings.FirstOrDefault(s=>String.Equals(s, "a"));
You can use LINQ (to objects):
List<string> strings = new List<string>(){"a","b","c"};
string aString = strings.Where(x => x.Equals("a")).FirstOrDefault();
The Where() methods iterates through the enumerable, and "returns" each element that satisfies the lambda. To get the first such element, you can chain on the FirstOrDefault() method (which will return default(string) if no elements meet the criteria.)
As #MichaelGraczyk points out, you can actually reduce the call to only FirstOrDefault(), since it has an overload that accepts a predicate:
string aString = strings.FirstOrDefault(x => x.Equals("a"));
There are a number of other useful methods available, which you can read about here.
It's kind of pointless in this particular example because you already know the string you want but in any case I think this is what you're trying to do...
List<string> strings = new List<string>(){"a","b","c"};
string aString = strings.Find((string s) => s == "a");
Is there a better—more functional, succinct, or elegant—way to write this? A reduce/fold function, perhaps?
var key = String.Join(String.Empty,
new[] {
keyRoot,
controllerName,
actionName
}.Concat(
from param in params
select param.Key + param.Value
)
);
The input is a few variables that are strings, as well as an enumerable of concatenated keys/values from a Dictionary<string, string>.
The output should be all of these strings concatenated.
It sounds like you could use the LINQ Aggregate function:
Using LINQ to concatenate strings
More readable to me would be something like this:
string key = string.Format("{0}{1}{2}{3}",
keyRoot,
controllerName,
actionName,
string.Join(string.Empty, parameters.Select( p => p.Key + p.Value)));
This might not be as "functional" but certainly as succinct and clear as I can come up with.
This doesn't improve it much...
var key = string.Concat(
new[] {
keyRoot,
controllerName,
actionName
}.Concat(
params.Select(kvp) => param.Key + param.Value)
).ToArray ()
);
This is 2 lines shorter if it doesn't have to be a single statement.
var list = new List<String> {
keyRoot,
controllerName,
actionName
};
list.AddRange (params.Select(kvp) => param.Key + param.Value));
var key = string.Concat(list.ToArray ());
I think for concatenating the sequence of all strings, your construct is already as functional as you can get.
Instead of using String.Join with an empty string, I'd probably use a StringBuilder together with a ForEach extenstion method like
public static class MyExtensions {
public static void ForEach(this IEnumerable<T> enumerable, Action<T> action) {
foreach (var entry in enumerable)
action(entry);
}
}
I also would define a local variable for the sequence like
var seq = new[] {
keyRoot,
controllerName,
actionName
}.Concat(
from param in params select param.Key + param.Value
);
var sb = new StringBuilder();
seq.ForEach(s=>sb.Append(s));
Of course, using the Aggregate function would be more "functional", but it is not more readable in my opinion plus it has performance penalties, because you need to construct intermediate strings...
With an extension to StringBuilder:
public static class StringBuilderExtensions {
public static StringBuilder AppendAll(this StringBuilder builder, IEnumerable<string> strings) {
foreach (string s in strings) builder.Append(s);
return builder;
}
}
it gets rather short and efficient:
string key =
new StringBuilder()
.Append(keyRoot)
.Append(controllerName)
.Append(actionName)
.AppendAll(parameters.Select(p => p.Key + p.Value))
.ToString();
This will build the string without creating any intermediate arrays.
One thing to improve would be to avoid the intermittent strings p.Key + p.Value by adding the key and value directly to the StringBuilder, but then the code gets less reusable.
Another thing to improve would be to set the capacity of the StringBuilder, but then you would need to loop though the dictionary and add upp the length of the strings first.
(Note: I used parameters for the name of the dictionary instead of params, as that is a keyword.)
Here is a solution in one expressions using Aggregate (effectively a fold):
var key = params.Aggregate(new StringBuilder()
.Append(keyRoot)
.Append(controllerName)
.Append(actionName),
(sb, p) => sb.Append(p.Key).Append(p.Value))
.ToString();
Somehow I can't seem to get string replacement within a foreach loop in C# to work. My code is as follows :
foreach (string s in names)
{
s.Replace("pdf", "txt");
}
Am still quite new to LINQ so pardon me if this sounds amateurish ;)
You say you're after a LINQ solution... that's easy:
var replacedNames = names.Select(x => x.Replace("pdf", "txt"));
We don't know the type of names, but if you want to assign back to it you could potentially use ToArray or ToList:
// If names is a List<T>
names = names.Select(x => x.Replace("pdf", "txt")).ToList();
// If names is an array
names = names.Select(x => x.Replace("pdf", "txt")).ToArray();
You should be aware that the code that you've posted isn't using LINQ at all at the moment though...
Strings in C# are immutable (does not change), so s.Replace will return a new string. Unfortunately this means you cannot use foreach to do the update. If names is an array this should work:
for(int i = 0; i < names.Length; i++)
{
names[i] = names[i].Replace("pdf", "txt");
}
As others have mentioned you'd need to use a for loop to do this in-place. However, if you don't need the operation to be done in-place (i.e. the results can be a different collection), then you could also do it as a linq query, e.g.
var results = from name in names select name.Replace("pdf", "txt");
One thing though - it looks like you are trying to change the extension of some file names. If that's what you are trying to do then I'd recommend Path.ChangeExtension which is specifically designed for this purpose.
var results = from name in names select Path.ChangeExtension(name, "txt");
s.Replace is a function so you would like s=s.Replace().. although it's better to use StringBuilder. (see upper answer)
Why use replace? It will make the application slow. Use regex instead:
http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex.replace.aspx
I'm stuck with using a web service I have no control over and am trying to parse the XML returned by that service into a standard object.
A portion of the XML structure looks like this
<NO>
<L>Some text here </L>
<L>Some additional text here </L>
<L>Still more text here </L>
</NO>
In the end, I want to end up with one String property that will look like "Some text here Some additional text here Still more text here "
What I have for an initial pass is what follows. I think I'm on the right track, but not quite there yet:
XElement source = \\Output from the Webservice
List<IndexEntry> result;
result = (from indexentry in source.Elements(entryLevel)
select new IndexEntry()
{
EtiologyCode = indexentry.Element("IE") == null ? null : indexentry.Element("IE").Value,
//some code to set other properties in this object
Note = (from l in indexentry.Elements("NO").Descendants
select l.value) //This is where I stop
// and don't know where to go
}
I know that I could add a ToList() operator at the end of that query to return the collection. Is there an opertaor or technique that would allow me to inline the concatentation of that collection to a single string?
Feel free to ask for more info if this isn't clear.
Thanks.
LINQ to XML is indeed the way here:
// Note: in earlier versions of .NET, string.Join only accepts
// arrays. In more modern versions, it accepts sequences.
var text = string.Join(" ", topElement.Elements("L").Select(x => x.Value));
EDIT: Based on the comment, it looks like you just need a single-expression way of representing this. That's easy, if somewhat ugly:
result = (from indexentry in source.Elements(entryLevel)
select new IndexEntry
{
EtiologyCode = indexentry.Element("IE") == null
? null
: indexentry.Element("IE").Value,
//some code to set other properties in this object
Note = string.Join(" ", indexentry.Elements("NO")
.Descendants()
.Select(x => x.Value))
};
Another alternative is to extract it into a separate extension method (it has to be in a top-level static class):
public static string ConcatenateTextNodes(this IEnumerable<XElement> elements) =>
string.Join(" ", elements.Select(x => x.Value));
then change your code to:
result = (from indexentry in source.Elements(entryLevel)
select new IndexEntry
{
EtiologyCode = indexentry.Element("IE") == null
? null
: indexentry.Element("IE").Value,
//some code to set other properties in this object
Note = indexentry.Elements("NO")
.Descendants()
.ConcatenateTextNodes()
}
EDIT: A note about efficiency
Other answers have suggested using StringBuilder in the name of efficiency. I would check for evidence of this being the right way to go before using it. If you think about it, StringBuilder and ToArray do similar things - they create a buffer bigger than they need to, add data to it, resize it when necessary, and come out with a result at the end. The hope is that you won't need to resize too often.
The difference between StringBuilder and ToArray here is what's being buffered - in StringBuilder it's the entire contents of the string you've built up so far. With ToArray it's just references. In other words, resizing the internal buffer used for ToArray is likely to be cheaper than resizing the one for StringBuilder, particularly if the individual strings are long.
After doing the buffering in ToArray, string.Join is hugely efficient: it can look through all the strings to start with, work out exactly how much space to allocate, and then concatenate it without ever having to copy the actual character data.
This is in sharp contrast to a previous answer I've given - but unfortunately I don't think I ever wrote up the benchmark.
I certainly wouldn't expect ToArray to be significantly slower, and I think it makes the code simpler here - no need to use side-effects etc, aggregation etc.
I don't have experience with it myself, but it strikes me that LINQ to XML could vastly simplify your code. Do a select of XML document, then loop through it and use a StringBuilder to append the L element to some string.
The other option is to use Aggregate()
var q = topelement.Elements("L")
.Select(x => x.Value)
.Aggregate(new StringBuilder(),
(sb, x) => return sb.Append(x).Append(" "),
sb => sb.ToString().Trim());
edit: The first lambda in Aggregate is the accumulator. This is taking all of your values and creating one value from them. In this case, it is creating a StringBuilder with your desired text. The second lambda is the result selector. This allows you to translate your accumulated value into the result you want. In this case, changing the StringBuilder to a String.
I like LINQ as much as the next guy, but you're reinventing the wheel here. The XmlElement.InnerText property does exactly what's being asked for.
Try this:
using System.Xml;
class Program
{
static void Main(string[] args)
{
XmlDocument d = new XmlDocument();
string xml =
#"<NO>
<L>Some text here </L>
<L>Some additional text here </L>
<L>Still more text here </L>
</NO>";
d.LoadXml(xml);
Console.WriteLine(d.DocumentElement.InnerText);
Console.ReadLine();
}
}