Is there an easy way to add a prefix or suffix to each member of an IEnumerable? I can't figure out a way besides (Where inputs is IEnumerable):
var sb = new StringBuilder();
foreach (var str in inputs) {
str = sb.Append(prefix).Append(str).ToString();
sb.clear();
}
But this won't let me assign back to the str... And I feel like there should be a better way to do this.
This should work:
var result = inputs
.Select(x => prefix + x)
.ToList();
It simply creates a new string for each input prepending the prefix and then put them into a list.For suffix just change prefix + x to x + suffix.
It worth to mention two wrong things with your code:
Your str variable is should be readonly inside foreach loop and you can't assign to it because strings are immutable and when you're calling sb.Append(prefix).Append(str).ToString(); it creates a new string;
You don't need StringBuilder for this kind of task because it efficient when you do several concatenations to one string. In your case you have several strings each of which you need to concatenate with some variable only once.
Strings are immutable, so you can't just modify them whenever you want. You can return a new list though:
IEnumberable<String> AppendString (IEnumerable<String> src, String append)
{
foreach (String str in src)
yield return str + append;
}
Related
I'm building a string based on an IEnumerable, and doing something like this:
public string BuildString()
{
var enumerable = GetEnumerableFromSomewhere(); // actually an in parameter,
// but this way you don't have to care
// about the type :)
var interestingParts = enumerable.Select(v => v.TheInterestingStuff).ToArray();
stringBuilder.Append("This is it: ");
foreach(var part in interestingParts)
{
stringBuilder.AppendPart(part);
if (part != interestingParts.Last())
{
stringBuilder.Append(", ");
}
}
}
private static void AppendPart(this StringBuilder stringBuilder, InterestingPart part)
{
stringBuilder.Append("[");
stringBuilder.Append(part.Something");
stringBuilder.Append("]");
if (someCondition(part))
{
// this is in reality done in another extension method,
// similar to the else clause
stringBuilder.Append(" = #");
stringBuilder.Append(part.SomethingElse");
}
else
{
// this is also an extension method, similar to this one
// it casts the part to an IEnumerable, and iterates over
// it in much the same way as the outer method.
stringBuilder.AppendInFilter(part);
}
}
I'm not entirely happy with this idiom, but I'm struggling to formulate something more succinct.
This is, of course, part of a larger string building operation (where there are several blocks similar to this one, as well as other stuff in between) - otherwise I'd probably drop the StringBuilder and use string.Join(", ", ...) directly.
My closest attempt at simplifying the above, though, is constructs like this for each iterator:
stringBuilder.Append(string.Join(", ", propertyNames.Select(prop => "[" + prop + "]")));
but here I'm still concatenating strings with +, which makes it feel like the StringBuilder doesn't really contribute much.
How could I simplify this code, while keeping it efficient?
You can replace this:
string.Join(", ", propertyNames.Select(prop => "[" + prop + "]"))
With c# 6 string interpolation:
string.Join(", ", propertyNames.Select(prop => $"[{prop}]"))
In both cases the difference is semantic only and it doesn't really matter. String concatenation like in your case in the select isn't a problem. The compiler still creates only 1 new string for it (and not 4, one for each segment and a 4th for the joint string).
Putting it all together:
var result = string.Join(", ", enumerable.Select(v => $"[{v.TheInterestingStuff}]"));
Because body of foreach is more complex that to fit in a String Interpolation scope you can just remove the last N characters of the string once calculated, as KooKiz suggested.
string separator = ", ";
foreach(var part in interestingParts)
{
stringBuilder.Append("[");
stringBuilder.Append(part);
stringBuilder.Append("]");
if (someCondition(part))
{
// Append more stuff
}
else
{
// Append other thingd
}
stringBuilder.Append(separator);
}
stringBuilder.Length = stringBuilder.Lenth - separator;
In any case I think that for better encapsulation the content of the loop's scope should sit in a separate function that will receive a part and the separator and will return the output string. It can also be an extension method for StringBuilder as suggested by user734028
Use Aggregate extension method with StringBuilder.
Will be more efficiently then concatenate strings if your collection are big
var builder = new StringBuilder();
list.Aggregate(builder, (sb, person) =>
{
sb.Append(",");
sb.Append("[");
sb.Append(person.Name);
sb.Append("]");
return sb;
});
builder.Remove(0, 1); // Remove first comma
As pure foreach is always more efficient then LINQ then just change logic for delimeter comma
var builder = new StringBuilder();
foreach(var part in enumerable.Select(v => v.TheInterestingStuff))
{
builder.Append(", ");
builder.Append("[");
builder.Append(part);
builder.Append("]");
}
builder.Remove(0, 2); //Remove first comma and space
Aggregate solution:
var answer = interestingParts.Select(v => "[" + v + "]").Aggregate((a, b) => a + ", " + b);
Serialization solution:
var temp = JsonConvert.SerializeObject(interestingParts.Select(x => new[] { x }));
var answer = temp.Substring(1, temp.Length - 2).Replace(",", ", ");
the code:
public string BuildString()
{
var enumerable = GetEnumerableFromSomewhere();
var interestingParts = enumerable.Select(v => v.TheInterestingStuff).ToArray();
stringBuilder.Append("This is it: ");
foreach(var part in interestingParts)
{
stringBuilder.AppendPart(part)
}
if (stringBuilder.Length>0)
stringBuilder.Length--;
}
private static void AppendPart(this StringBuilder stringBuilder, InterestingPart part)
{
if (someCondition(part))
{
stringBuilder.Append(string.Format("[{0}] = #{0}", part.Something));
}
else
{
stringBuilder.Append(string.Format("[{0}]", part.Something));
stringBuilder.AppendInFilter(part); //
}
}
much better now IMO.
Now a little discussion on making it very fast. We can use Parallel.For. But you would think (if you would think) the Appends are all happening to a single shareable resource, aka the StringBuilder, and then you would have to lock it to Append to it, not so efficient! Well, if we can say that each iteration of the for loop in the outer function creates one single string artifact, then we can have a single array of string, allocated to the count of interestingParts before the Parallel for starts, and each index of the Parallel for would store its string to its respective index.
Something like:
string[] iteration_buckets = new string[interestingParts.Length];
System.Threading.Tasks.Parallel.For(0, interestingParts.Length,
(index) =>
{
iteration_buckets[index] = AppendPart(interestingParts[index]);
});
your function AppendPart will have to be adjusted to make it a non-extension to take just a string and return a string.
After the loop ends you can do a string.Join to get a string, which is what you may be doing with the stringBuilder.ToString() too.
I breezed through the documentation for the string class and didn't see any good tools for combining an arbitrary number of strings into a single string. The best procedure I could come up with in my program is
string [] assetUrlPieces = { Server.MapPath("~/assets/"),
"organizationName/",
"categoryName/",
(Guid.NewGuid().ToString() + "/"),
(Path.GetFileNameWithoutExtension(file.FileName) + "/")
};
string assetUrl = combinedString(assetUrlPieces);
private string combinedString ( string [] pieces )
{
string alltogether = "";
foreach (string thispiece in pieces) alltogether += alltogether + thispiece;
return alltogether;
}
but that seems like too much code and too much inefficiency (from the string addition) and awkwardness.
If you want to insert a separator between values, string.Join is your friend. If you just want to concatenate the strings, then you can use string.Concat:
string assetUrl = string.Concat(assetUrlPieces);
That's marginally simpler (and possibly more efficient, but probably insignificantly) than calling string.Join with an empty separator.
As noted in comments, if you're actually building up the array at the same point in the code that you do the concatenation, and you don't need the array for anything else, just use concatenation directly:
string assetUrl = Server.MapPath("~/assets/") +
"organizationName/" +
"categoryName/" +
Guid.NewGuid() + "/" +
Path.GetFileNameWithoutExtension(file.FileName) + "/";
... or potentially use string.Format instead.
I prefer using string.Join:
var result = string.Join("", pieces);
You can read about string.Join on MSDN
You want a StringBuilder, I think.
var sb = new StringBuilder(pieces.Count());
foreach(var s in pieces) {
sb.Append(s);
}
return sb.ToString();
Update
#FiredFromAmazon.com: I think you'll want to go with the string.Concat solution offered by others for
Its sheer simplicity
Higher performance. Under the hood, it uses FillStringChecked, which does pointer copies, whereas string.Join uses StringBuilder. See http://referencesource.microsoft.com/#mscorlib/system/string.cs,1512. (Thank you to #Bas).
string.Concat is the most appropriate method for what you want.
var result = string.Concat(pieces);
Unless you want to put delimiters between the individual strings. Then you'd use string.Join
var result = string.Join(",", pieces); // comma delimited result.
A simple way to do this with a regular for loop:
(since you can use the indices, plus I like these loops better than foreach loops)
private string combinedString(string[] pieces)
{
string alltogether = "";
for (int index = 0; index <= pieces.Length - 1; index++) {
if (index != pieces.Length - 1) {
alltogether += string.Format("{0}/" pieces[index]);
}
}
return alltogether;
I have list of strings. I want to convert each element of it to single quoted string (i.e "ABC" --> 'ABC'), How to do this in .net.
Thanks,
Omkar
Linq can help here.
var newList = oldList.Select(c => c.Replace("\"", "'"));
This is already well answered. However, I have the hunch that you are taking a list of strings in C#, then trying to build an SQL expression for use in IN statements, e.g.:
SELECT * FROM table WHERE name IN ('John','Mary','Peter')
In that case, you'd need to join the strings together, as well as protect from code injection attacks by doubling any single-quote characters.
StringBuilder sb = new StringBuilder();
foreach (string entry in list) {
if (sb.Length > 0) sb.Append(",");
sb.Append("\'" + entry.Replace("'","''") + "\'");
}
string expr = sb.ToString();
You'd also need to handle the special case when the list is empty because IN () is not a valid syntax for SQL.
If this is not what you want, just ignore me. :-)
I assume you have regular strings s to 's' (quoted string) and you wanted a List<> to be converted.
List<string> stringList = new List<string>();
//Fill the list with strings here.
var query = from str in stringList
select string.Format("\'{0}\'", str);
List<string> quotedList = query.ToList<string>();
If you want to replace all double with single quotes, simply do this:
myString = myString.Replace( "\"", "'" );
However, note that ' is not a valid string delimiter in C#, so you can't have the string 'ABC', but you can have the string "'ABC'" that contains the text 'ABC'
EDIT
When looking at Geoff's answer, I saw that you wanted a list. In that case, his answer is almost correct- Try this variant instead:
var convertedList = myStringList.Select(s => s = s.Replace("\"", "'").ToList();
Suppose I have a collection of strings:
"foo"
"bar"
"xyz"
And I would like to generate a comma separated values from the list into something like:
"foo, bar, xyz"
Notice the lack of ", " at the end.
I am aware that there are dozens of ways to generate this:
use for-loop and string.Format() or StringBuilder.
use integer counter and remove the ending ", " if the value > 0
don't put ", " on the first run
etc.
Sample code of what I have right now:
if (strs.Count() > 0)
{
var sb = new StringBuilder();
foreach (var str in strs)
sb.AppendFormat("{0}, ", str);
return sb.Remove(0, 2).ToString();
}
What is the best code that is highly reusable for the above scenario, and why?
You want to use the string.Join method, which exists in the BCL for this purpose.
Example:
var myArray = new string[] { "one", "two", "three" };
var output = string.Join(", ", myArray);
Or if you're using .NET 3.5, you can do this with any IEnumerable<string> as such:
var output = string.Join(", ", myEnumerable.ToArray());
(Note that this doesn't give the best performance as it requires, although it is clearly still 'O(n)', and should be suitable for almost all cases).
Now, if your enumerable is not of type string (generically an IEnumerable<T>), you can just use the Select method to convert the result into a string, e.g.
var output = string.Join(", ", myEnumerable.Select(e => e.ToString()).ToArray());
I'm not sure if you're dealing with values that may potentially contains commas in themselves, but this can be worked around by enclosing them in quotes (") and escaping the quotes, similarly to the CSV format.
var output = string.Join(", ", items.Select(x => x.Contains(",") ?
"\"" + x.Replace("\"", "\"\"") + "\"" : x);
Of course, splitting them back up again is a slightly triciker task, which requires a bit of regex.
String.Join is the right answer, but in the case of an IEnumerable, Linq is often shorter than a for loop:
someStringCollection.Aggregate((first, second) => first + ", " + second);
As others have said: String.Join is normally the best way to do this. But what if you have just have an IEnumerable rather than an array? Perhaps you have code to enumerate these as you read them from a file using deferred execution. In this case String.Join isn't quite as nice, because you have to loop over the strings twice — once to create the array and once to join it. In that case, you want something more like this:
public static string ToDelimitedString(this IEnumerable<string> source, string delimiter)
{
string d = "";
var result = new StringBuilder();
foreach( string s in source)
{
result.Append(d).Append(s);
d = delimiter;
}
return result.ToString();
}
This will perform almost as well as String.Join, and works in the more general case. Then, given a string array or any other IEnumerable you can call it like this:
string finalStr = MyStrings.ToDelimitedString(",");
string finalstr = String.Join(",", strs);
Use
string s = string.Join (",", new string[] {"aaa", "bbb", "ccc"});
Use String.Join
If you have an array of strings, go with Noldorin's solution.
But if it's some other collection type, I might do this:
if (strs.Count() > 0)
{
var sb = new StringBuilder();
foreach (var str in strs)
sb.AppendFormat("{0} {1}", (0 == sb.Length ? "" : ","), str);
return sb.ToString();
}
string to build up using keyvaluepair is like this: "name1=v1&name2=v2&name3=v3"
what i am doing:
var sb = new StringBuilder();
foreach (var name in nameValues)
{
sb.AppendFormat("{0}={1}&", name.Key, name.Value);
}
//remove last '&' sign, this is what i think is ugly
sb.ToString().Remove(lastIndex);
any elegant way to avoid the last removal statement of '&' sign?
var joined =
String.Join("&", nameValues.Select(n => n.Key + "=" + n.Value).ToArray());
Given that we're not concatenating to one big string (we're producing many small strings) concatenation carries no performace penalties in this case. And in .NET strings are length prefixed anyway so the whole concatenation performance issue is less relevant than in C. String.Join() is very fast as well, faster than StringBuilder.
TLDR: Use String.Join()
Take a look here: How to build a query string for a URL in C#?; Quoting:
private string ToQueryString(NameValueCollection nvc)
{
return "?" +
string.Join("&",
Array.ConvertAll(
nvc.AllKeys,
key => String.Format("{0}={1}", HttpUtility.UrlEncode(key),
HttpUtility.UrlEncode(nvc[key]))));
}
foreach (var name in nameValues)
{
if (sb.Length > 0) sb.Append("&");
sb.AppendFormat("{0}={1}", name.Key, name.Value);
}
Just add "&" when needed, do not remove it from end.
Here's another approach which I've sometimes used:
var sb = new StringBuilder();
string prefix = "";
foreach (var name in nameValues)
{
sb.Append(prefix);
prefix = "&";
sb.AppendFormat("{0}={1}", name.Key, name.Value);
}
It's just a way of prepending & before every pair other than the first one without using a conditional test.
If you want to use your original idea of trimming the StringBuilder by the way, I'd suggest the following code instead:
sb.Length--; // Remove the last character
return sb.ToString();
I tend to use this, utilising the fact you can truncate a string builder with a decrement on the length property:
var sb = new StringBuilder();
foreach (var name in nameValues)
{
sb.AppendFormat("{0}={1}&", name.Key, name.Value);
}
if (sb.Length > 0) sb.Length--;
Well at least you can remove the & sign before the ToString() call by doing --sb.Length;
var sb = new StringBuilder();
sb.AppendFormat("{0}={1}", nameValues[0].Key, nameValues[0].Value);
for (int i = 1; i < nameValues.Count; i++)
{
sb.AppendFormat("&{0}={1}", nameValues[i].Key, nameValues[i].Value);
}