I have a collection like this
List<int> {1,15,17,8,3};
how to get a flat string like "1-15-17-8-3" through LINQ query?
thank you
something like...
string mystring = string.Join("-", yourlist.Select( o => o.toString()).toArray()));
(Edit: Now its tested, and works fine)
You can write an extension method and then call .ToString("-") on your IEnumerable object type as shown here:
int[] intArray = { 1, 2, 3 };
Console.WriteLine(intArray.ToString(","));
// output 1,2,3
List<string> list = new List<string>{"a","b","c"};
Console.WriteLine(intArray.ToString("|"));
// output a|b|c
Examples of extension method implementation are here:
http://coolthingoftheday.blogspot.com/2008/09/todelimitedstring-using-linq-and.html
http://www.codemeit.com/linq/c-array-delimited-tostring.html
Use Enumerable.Aggregate like so:
var intList = new[] {1,15,17,8,3};
string result = intList.Aggregate(string.Empty, (str, nextInt) => str + nextInt + "-");
This is the standard "LINQy" way of doing it - what you're wanting is the aggregate. You would use the same concept if you were coding in another language, say Python, where you would use reduce().
EDIT:
That will get you "1-15-17-8-3-". You can lop off the last character to get what you're describing, and you can do that inside of Aggregate(), if you'd like:
string result = intList.Aggregate(string.Empty, (str, nextInt) => str + nextInt + "-", str => str.Substring(0, str.Length - 1));
The first argument is the seed, the second is function that will perform the aggregation, and the third argument is your selector - it allows you to make a final change to the aggregated value - as an example, your aggregate could be a numeric value and you want return the value as a formatted string.
HTH,
-Charles
StringBuilder sb = new StringBuilder();
foreach(int i in collection)
{
sb.Append(i.ToString() + "-");
}
string result = sb.ToString().SubString(0,sb.ToString().ToCharArray().Length - 2);
Something like this perhaps (off the top of my head that is!).
The best answer is given by Tim J.
If, however, you wanted a pure LINQ solution then try something like this (much more typing, and much less readable than Tim J's answer):
string yourString = yourList.Aggregate
(
new StringBuilder(),
(sb, x) => sb.Append(x).Append("-"),
sb => (sb.Length > 0) ? sb.ToString(0, sb.Length - 1) : ""
);
(This is a variation on Charles's answer, but uses a StringBuilder rather than string concatenation.)
Related
This question already has answers here:
Fastest way to trim a string and convert it to lower case
(6 answers)
Closed 6 years ago.
I am searching for a simple way to remove underscores from strings and replacing the next character with its upper case letter.
For example:
From: "data" to: "Data"
From: "data_first" to: "DataFirst"
From: "data_first_second" to: "DataFirstSecond"
Who needs more than one line of code?
var output = Regex.Replace(input, "(?:^|_)($|.)", m => m.Groups[1].Value.ToUpper());
This approach is known as a "finite-state machine" that iterates through the string - in that it has a finite set of states ("is the first letter of a word following an underscore" vs "character inside a word"). This represents the minimal instructions needed to perform the task. You can use a Regular Expression for the same effect, but it would generate at least the same number of instructions at runtime. Writing the code out manually guarantees a minimal runtime.
The advantage of this approach is sheer performance: there is no unnecessary allocation of intermediate strings being performed, and it iterates through the input string only once, giving a time complexity of O(n) and a space complexity of O(n). This cannot be improved upon.
public static String ConvertUnderscoreSeparatedStringToPascalCase(String input) {
Boolean isFirstLetter = true;
StringBuilder output = new StringBuilder( input.Length );
foreach(Char c in input) {
if( c == '_' ) {
isFirstLetter = true;
continue;
}
if( isFirstLetter ) {
output.Append( Char.ToUpper( c ) );
isFirstLetter = false;
}
else {
output.Append( c );
}
}
return output.ToString();
}
You can use String.Split and following LINQ query:
IEnumerable<string> newStrings = "data_first_second".Split('_')
.Select(t => new String(t.Select((c, index) => index == 0 ? Char.ToUpper(c) : c).ToArray()));
string result = String.Join("", newStrings);
All other answers valid... for a culture-aware way:
var textInfo = CultureInfo.CurrentCulture.TextInfo;
var modifiedString = textInfo.ToTitleCase(originalString).Replace("_","")
I've made a fiddle: https://dotnetfiddle.net/NAr5PP
I would do something like this:
string test = "data_first_second";
string[] testArray=test.Split('_');
StringBuilder modifiedString = new StringBuilder();
foreach (string t in testArray)
{
modifiedString.Append(t.First().ToString().ToUpper() + t.Substring(1));
}
test=modifiedString.toString();
Use LINQ and Split method like this:
var result = string.Join("",str.Split('_')
.Select(c => c.First().ToString()
.ToUpper() + String.Join("", c.Skip(1))));
I've got collection of words, and i wanna create collection from this collection limited to 5 chars
Input:
Car
Collection
Limited
stackoverflow
Output:
car
colle
limit
stack
word.Substring(0,5) throws exception (length)
word.Take(10) is not good idea, too...
Any good ideas ??
LINQ to objects for this scenario? You can do a select as in this:
from w in words
select new
{
Word = (w.Length > 5) ? w.Substring(0, 5) : w
};
Essentially, ?: gets you around this issue.
var words = new [] { "Car", "Collection", "Limited", "stackoverflow" };
IEnumerable<string> cropped = words.Select(word =>
word[0..Math.Min(5, word.Length)]);
Ranges are available in C# 8, otherwise you'll need to do:
IEnumerable<string> cropped = words.Select(word =>
word.Substring(0, Math.Min(5, word.Length)));
Something you can do, is
string partialText = text.Substring(0, Math.Min(text.Length, 5));
I believe the kind of answer you were looking for would look like this:
var x = new string[] {"car", "Collection", "Limited", "stackoverflow" };
var output = x.Select(word => String.Join("", word.Take(5).ToList()));
The variable "output" contains the result:
car
Colle
Limit
stack
and the string "car" doesn't throw an exception.
But while Join and Take(5) works, it's generally much simpler to use, as was suggested in another answer,
subString = word.Substring(0,Math.Min(5,word.Length));
The latter code is more human-readable and lightweight, though there is definitely a slight coolness factor to using Linq on a string to take the first five characters, without having to check the length of the string.
I have a string "8329874566".
I want to place - in the string like this "832-98-4566"
Which string function can I use?
I would have done something like this..
string value = "8329874566";
value = value.Insert(6, "-").Insert(3, "-");
You convert it to a number and then format the string.
What I like most about this is it's easier to read/understand what's going on then using a few substring methods.
string str = "832984566";
string val = long.Parse(str).ToString("###-##-####");
There may be a tricky-almost-unreadable regex solution, but this one is pretty readable, and easy.
The first parameter of the .Substring() method is where you start getting the characters, and the second is the number of characters you want to get, and not giving it sets a default as value.length -1 (get chars until the end of the string):
String value = "8329874566";
String Result = value.Substring(0,3) + "-" + value.Substring(3,2) + "-" + value.Substring(6);
--[edit]--
Just noticed you didn't use one of the numbers AT ALL (number '7') in the expected result example you gave, but if you want it, just change the last substring as "5", and if you want the '7' but don't want 5 numbers in the last set, let it like "5,4".
Are you trying to do this like American Social Security numbers? I.e., with a hyphen after the third and and fifth numerals? If so:
string s = "8329874566";
string t = String.Format("{0}-{1}-{2}", s.Substring(0, 3), s.Substring(3, 2), s.Substring(5));
Just out of completeness, a regular expression variant:
Regex.Replace(s, #"(\d{3})(\d{2})(\d{4})", "$1-$2-$3");
I consider the Insert variant to be the cleanest, though.
This works fine, and I think that is more clear:
String value = "8329874566";
value = value.Insert(3, "-").Insert(6, "-");
The console outputs shows this:
832-98-74566
If the hyphens are to go in the same place each time, then you could simply concatenate together the pieces of the orginal string like this:
// 0123456789 <- index
string number = "8329874566";
string new = number.Substring(0, 3) + "-" + number.Substring(3, 2) + "-" + number.Substring(5);
For a general way of making mutable strings, use the StringBuilder class. This allows deletions and insertions to be made before calling ToString to produce the final string.
You could try the following:
string strNumber = "8329874566"
string strNewNumber = strNumber.Substring(0,3) + "-" + strNumber.Substring(4,2) + "-" strNumber.Substring(6)
or something in this manner
string val = "832984566";
string result = String.Format("{0}-{1}-{2}", val.Substring(0,3), val.Substring(3,2), val.Substring(5,4));
var result = string.Concat(value.Substring(0,3), "-", value.Substring(3,2), "-", value.Substring(5,4));
or
var value = "8329874566".Insert(3, "-").Insert(6, "-");
Now how about this for a general solution?
// uglified code to fit within horizontal limits
public static string InsertAtIndices
(this string original, string insertion, params int[] insertionPoints) {
var mutable = new StringBuilder(original);
var validInsertionPoints = insertionPoints
.Distinct()
.Where(i => i >= 0 && i < original.Length)
.OrderByDescending(i => i);
foreach (int insertionPoint in validInsertionPoints)
mutable.Insert(insertionPoint, insertion);
return mutable.ToString();
}
Usage:
string ssn = "832984566".InsertAtIndices("-", 3, 5);
string crazy = "42387542342309856340924803"
.InsertAtIndices(":", 1, 2, 3, 4, 5, 6, 17, 200, -1, -1, 2, 3, 3, 4);
Console.WriteLine(ssn);
Console.WriteLine(crazy);
Output:
832-98-4566
4:2:3:8:7:5:42342309856:340924803
Overkill? Yeah, maybe...
P.S. Yes, I am regex illiterate--something I hope to rectify someday.
A straightforward (but not flexible) approach would be looping over the characters of the string while keeping a counter running. You can then construct a new string character by character. You can add the '-' character after the 3rd and 5th character.
A better approach may be to use a function to insert a single character in the middle of the string at a specific index. String.Insert() would do well. The only thing to pay attention to here is that the string indexes will get off by one with each insert.
EDIT more language-specific as per comments
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();
}
For logging purposes, I would like to call the .ToString() method of every object on an object[] array. How can I do this in the simplest way?
Say I have :
myArray = new Object[]{"astring",1, Customer}
Log(????);
How can I pass a string such as its value is equal to:
"astring".ToString()+1.ToString()+Customer.ToString()
Or better, with comma between each value.
Like this:
Log(String.Join(", ", myArray.Select(o => o.ToString()).ToArray()));
Update:
From framework 4 the Join method can also take an IEnumerable<string>, so you don't need the ToArray:
Log(String.Join(", ", myArray.Select(o => o.ToString())));
MoreLINQ has a ToDelimitedString method for precisely this purpose.
It uses a StringBuilder rather than using String.Join (from what I remember from previous questions, the efficiency of the two approaches depends heavily on what the input is) but it's simple enough. Here's the core code (there are a couple of wrappers to allow a default delimiter):
private static string ToDelimitedStringImpl<TSource>
(IEnumerable<TSource> source, string delimiter)
{
Debug.Assert(source != null);
Debug.Assert(delimiter != null);
var sb = new StringBuilder();
foreach (var value in source)
{
if (sb.Length > 0) sb.Append(delimiter);
sb.Append(value);
}
return sb.ToString();
}
I regularly use...
String.Join(", ", Array.ConvertAll<object, string>(myArray, Convert.ToString))
A simple old fashion way :
string myString = "";
foreach(Object o in myArray)
myString += o.ToString() + ", ";
// Remove the extra comma
if(myString.Length >=2)
myString.Remove(myString.Length - 2);